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

libplasma

applet.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2005 by Aaron Seigo <aseigo@kde.org>
00003  *   Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
00004  *   Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
00005  *
00006  *   This program is free software; you can redistribute it and/or modify
00007  *   it under the terms of the GNU Library General Public License as
00008  *   published by the Free Software Foundation; either version 2, or
00009  *   (at your option) any later version.
00010  *
00011  *   This program is distributed in the hope that it will be useful,
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *   GNU General Public License for more details
00015  *
00016  *   You should have received a copy of the GNU Library General Public
00017  *   License along with this program; if not, write to the
00018  *   Free Software Foundation, Inc.,
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020  */
00021 
00022 #include "applet.h"
00023 #include "applet_p.h"
00024 
00025 #include <cmath>
00026 #include <limits>
00027 
00028 #include <QAction>
00029 #include <QApplication>
00030 #include <QDesktopWidget>
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 <QTimer>
00044 #include <QUiLoader>
00045 
00046 #include <KAction>
00047 #include <KIcon>
00048 #include <KColorScheme>
00049 #include <KConfigDialog>
00050 #include <KDialog>
00051 #include <KIconLoader>
00052 #include <KPluginInfo>
00053 #include <KStandardDirs>
00054 #include <KService>
00055 #include <KServiceTypeTrader>
00056 #include <KShortcut>
00057 #include <KWindowSystem>
00058 #include <KActionCollection>
00059 
00060 #include <Solid/PowerManagement>
00061 
00062 #include "plasma/configxml.h"
00063 #include "plasma/containment.h"
00064 #include "plasma/containment_p.h"
00065 #include "plasma/corona.h"
00066 #include "plasma/dataenginemanager.h"
00067 #include "plasma/package.h"
00068 #include "plasma/packages_p.h"
00069 #include "plasma/plasma.h"
00070 #include "plasma/scripting/appletscript.h"
00071 #include "plasma/shadowitem_p.h"
00072 #include "plasma/svg.h"
00073 #include "plasma/panelsvg.h"
00074 #include "plasma/theme.h"
00075 #include "plasma/toolbox_p.h"
00076 #include "plasma/view.h"
00077 #include "plasma/widgets/label.h"
00078 #include "plasma/widgets/pushbutton.h"
00079 
00080 //#define DYNAMIC_SHADOWS
00081 namespace Plasma
00082 {
00083 
00084 Applet::Applet(QGraphicsItem *parent,
00085                const QString& serviceID,
00086                uint appletId)
00087     :  QGraphicsWidget(parent),
00088        d(new AppletPrivate(KService::serviceByStorageId(serviceID), appletId, this))
00089 {
00090     // WARNING: do not access config() OR globalConfig() in this method!
00091     //          that requires a scene, which is not available at this point
00092     d->init();
00093 }
00094 
00095 Applet::Applet(QObject* parentObject, const QVariantList& args)
00096     :  QGraphicsWidget(0),
00097        d(new AppletPrivate(KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString()),
00098                      args.count() > 1 ? args[1].toInt() : 0, this))
00099 {
00100     // now remove those first two items since those are managed by Applet and subclasses shouldn't
00101     // need to worry about them. yes, it violates the constness of this var, but it lets us add
00102     // or remove items later while applets can just pretend that their args always start at 0
00103     QVariantList &mutableArgs = const_cast<QVariantList&>(args);
00104     if (!mutableArgs.isEmpty()) {
00105         mutableArgs.removeFirst();
00106 
00107         if (!mutableArgs.isEmpty()) {
00108             mutableArgs.removeFirst();
00109         }
00110     }
00111 
00112     setParent(parentObject);
00113     // WARNING: do not access config() OR globalConfig() in this method!
00114     //          that requires a scene, which is not available at this point
00115     d->init();
00116 
00117     // the brain damage seen in the initialization list is due to the
00118     // inflexibility of KService::createInstance
00119 }
00120 
00121 Applet::~Applet()
00122 {
00123     if (d->transient) {
00124         d->resetConfigurationObject();
00125     }
00126 
00127     delete d;
00128 }
00129 
00130 PackageStructure::Ptr Applet::packageStructure()
00131 {
00132     if (!AppletPrivate::packageStructure) {
00133         AppletPrivate::packageStructure = new PlasmoidPackage();
00134     }
00135 
00136     return AppletPrivate::packageStructure;
00137 }
00138 
00139 void Applet::init()
00140 {
00141     if (d->script && !d->script->init()) {
00142         setFailedToLaunch(true, i18n("Script initialization failed"));
00143     }
00144 }
00145 
00146 uint Applet::id() const
00147 {
00148     return d->appletId;
00149 }
00150 
00151 void Applet::save(KConfigGroup &g) const
00152 {
00153     KConfigGroup group = g;
00154     if (!group.isValid()) {
00155         group = *d->mainConfigGroup();
00156     }
00157 
00158     // we call the dptr member directly for locked since isImmutable()
00159     // also checks kiosk and parent containers
00160     group.writeEntry("immutability", (int)d->immutability);
00161     group.writeEntry("plugin", pluginName());
00162     //FIXME: for containments, we need to have some special values here w/regards to
00163     //       screen affinity (e.g. "bottom of screen 0")
00164     //kDebug() << pluginName() << "geometry is" << geometry() << "pos is" << pos() << "bounding rect is" << boundingRect();
00165     group.writeEntry("geometry", geometry());
00166     group.writeEntry("zvalue", zValue());
00167 
00168     if (transform() == QTransform()) {
00169         group.deleteEntry("transform");
00170     } else {
00171         QList<qreal> m;
00172         QTransform t = transform();
00173         m << t.m11() << t.m12() << t.m13() << t.m21() << t.m22() << t.m23() << t.m31() << t.m32() << t.m33();
00174         group.writeEntry("transform", m);
00175         //group.writeEntry("transform", transformToString(transform()));
00176     }
00177 
00178     KConfigGroup appletConfigGroup(&group, "Configuration");
00179     //FIXME: we need a global save state too
00180     saveState(appletConfigGroup);
00181 
00182     if (d->activationAction) {
00183         KConfigGroup shortcutConfig(&group, "Shortcuts");
00184         shortcutConfig.writeEntry("global", d->activationAction->globalShortcut().toString());
00185     }
00186 }
00187 
00188 void Applet::restore(KConfigGroup &group)
00189 {
00190     QList<qreal> m = group.readEntry("transform", QList<qreal>());
00191     if (m.count() == 9) {
00192         QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
00193         setTransform(t);
00194     }
00195 
00196     qreal z = group.readEntry("zvalue", 0);
00197 
00198     if (z >= AppletPrivate::s_maxZValue) {
00199         AppletPrivate::s_maxZValue = z;
00200     }
00201 
00202     if (z > 0) {
00203         setZValue(z);
00204     }
00205 
00206     setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
00207 
00208     QRectF geom = group.readEntry("geometry", QRectF());
00209     if (geom.isValid()) {
00210         setGeometry(geom);
00211     }
00212 
00213     KConfigGroup shortcutConfig(&group, "Shortcuts");
00214     QString shortcutText = shortcutConfig.readEntry("global", QString());
00215     if (!shortcutText.isEmpty()) {
00216         setGlobalShortcut(KShortcut(shortcutText));
00217     }
00218 
00219     // local shortcut, if any
00220     //TODO: implement; the shortcut will need to be registered with the containment
00221     /*
00222     shortcutText = shortcutConfig.readEntry("local", QString());
00223     if (!shortcutText.isEmpty()) {
00224         //TODO: implement; the shortcut 
00225     }
00226     */
00227 }
00228 
00229 void AppletPrivate::setFocus()
00230 {
00231     kDebug() << "setting focus";
00232     q->setFocus(Qt::ShortcutFocusReason);
00233 }
00234 
00235 void Applet::setFailedToLaunch(bool failed, const QString& reason)
00236 {
00237     if (d->failed == failed) {
00238         return;
00239     }
00240 
00241     d->failed = failed;
00242     prepareGeometryChange();
00243 
00244     qDeleteAll(QGraphicsItem::children());
00245     setLayout(0);
00246 
00247     if (failed) {
00248         setBackgroundHints(d->backgroundHints|StandardBackground);
00249 
00250         QGraphicsLinearLayout *failureLayout = new QGraphicsLinearLayout();
00251         failureLayout->setContentsMargins(0, 0, 0, 0);
00252         Label *failureWidget = new Plasma::Label(this);
00253         failureWidget->setText(d->visibleFailureText(reason));
00254         QLabel *label = failureWidget->nativeWidget();
00255         label->setWordWrap(true);
00256         failureLayout->addItem(failureWidget);
00257         setLayout(failureLayout);
00258         resize(300,250);
00259         setMinimumSize(failureWidget->size());
00260         //resize(label->size());
00261         d->background->resizePanel(geometry().size());
00262     }
00263     update();
00264 }
00265 
00266 void Applet::saveState(KConfigGroup &group) const
00267 {
00268     if (group.config()->name() != config().config()->name()) {
00269         // we're being saved to a different file!
00270         // let's just copy the current values in our configuration over
00271         KConfigGroup c = config();
00272         c.copyTo(&group);
00273     }
00274 }
00275 
00276 KConfigGroup Applet::config(const QString &group) const
00277 {
00278     KConfigGroup cg = config();
00279     return KConfigGroup(&cg, group);
00280 }
00281 
00282 KConfigGroup Applet::config() const
00283 {
00284     if (d->isContainment) {
00285         return *(d->mainConfigGroup());
00286     }
00287 
00288     return KConfigGroup(d->mainConfigGroup(), "Configuration");
00289 }
00290 
00291 KConfigGroup Applet::globalConfig() const
00292 {
00293     KConfigGroup globalAppletConfig;
00294     const Containment *c = containment();
00295     QString group = isContainment() ? "ContainmentGlobals" : "AppletGlobals";
00296 
00297     if (c && c->corona()) {
00298         KSharedConfig::Ptr coronaConfig = c->corona()->config();
00299         globalAppletConfig = KConfigGroup(coronaConfig, group);
00300     } else {
00301         globalAppletConfig = KConfigGroup(KGlobal::config(), group);
00302     }
00303 
00304     return KConfigGroup(&globalAppletConfig, d->globalName());
00305 }
00306 
00307 void Applet::destroy()
00308 {
00309     if (immutability() != Mutable || d->transient) {
00310         return; //don't double delete
00311     }
00312 
00313     d->transient = true;
00314 
00315     if (isContainment()) {
00316         d->cleanUpAndDelete();
00317     } else {
00318         connect(Animator::self(), SIGNAL(animationFinished(QGraphicsItem*,Plasma::Animator::Animation)),
00319                 this, SLOT(appletAnimationComplete(QGraphicsItem*,Plasma::Animator::Animation)));
00320         Animator::self()->animateItem(this, Animator::DisappearAnimation);
00321     }
00322 }
00323 
00324 void AppletPrivate::appletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim)
00325 {
00326     if (anim != Animator::DisappearAnimation || item != q) {
00327         return; //it's not our time yet
00328     }
00329 
00330     cleanUpAndDelete();
00331 }
00332 
00333 void AppletPrivate::selectItemToDestroy()
00334 {
00335     //FIXME: this will not work nicely with multiple screens and being zoomed out!
00336     if (q->isContainment() &&
00337         q->view() && q->view()->transform().isScaling() &&
00338         q->scene()->focusItem() != q) {
00339         QGraphicsItem *focus = q->scene()->focusItem();
00340 
00341         if (focus) {
00342             Containment *toDestroy = dynamic_cast<Containment*>(focus->topLevelItem());
00343 
00344             if (toDestroy) {
00345                 toDestroy->destroy();
00346                 return;
00347             }
00348         }
00349     }
00350 
00351     q->destroy();
00352 }
00353 
00354 void AppletPrivate::cleanUpAndDelete()
00355 {
00356     //kDebug() << "???????????????? DESTROYING APPLET" << name() << " ???????????????????????????";
00357     QGraphicsWidget *parent = dynamic_cast<QGraphicsWidget *>(q->parentItem());
00358     //it probably won't matter, but right now if there are applethandles, *they* are the parent.
00359     //not the containment.
00360 
00361     //is the applet in a containment and is the containment have a layout? if yes, we remove the applet in the layout
00362     if (parent && parent->layout()) {
00363         QGraphicsLayout *l = parent->layout();
00364         for (int i = 0; i < l->count(); ++i) {
00365             if (q == l->itemAt(i)) {
00366                 l->removeAt(i);
00367                 break;
00368             }
00369         }
00370     }
00371 
00372     if (configXml) {
00373         configXml->setDefaults();
00374     }
00375 
00376     q->scene()->removeItem(q);
00377     q->deleteLater();
00378 }
00379 
00380 ConfigXml* Applet::configScheme() const
00381 {
00382     return d->configXml;
00383 }
00384 
00385 DataEngine* Applet::dataEngine(const QString& name) const
00386 {
00387     int index = d->loadedEngines.indexOf(name);
00388     if (index != -1) {
00389         return DataEngineManager::self()->engine(name);
00390     }
00391 
00392     DataEngine* engine = DataEngineManager::self()->loadEngine(name);
00393     if (engine->isValid()) {
00394         d->loadedEngines.append(name);
00395     }
00396 
00397     return engine;
00398 }
00399 
00400 const Package* Applet::package() const
00401 {
00402     return d->package;
00403 }
00404 
00405 QGraphicsView *Applet::view() const
00406 {
00407     // It's assumed that we won't be visible on more than one view here.
00408     // Anything that actually needs view() should only really care about
00409     // one of them anyway though.
00410     if (!scene()) {
00411         return 0;
00412     }
00413 
00414     foreach (QGraphicsView *view, scene()->views()) {
00415         if (view->sceneRect().intersects(sceneBoundingRect()) ||
00416             view->sceneRect().contains(scenePos())) {
00417             return view;
00418         }
00419     }
00420     return 0;
00421 }
00422 
00423 QRectF Applet::mapFromView(const QGraphicsView *view, const QRect &rect) const
00424 {
00425     // Why is this adjustment needed? Qt calculation error?
00426     return mapFromScene(view->mapToScene(rect)).boundingRect().adjusted(0, 0, 1, 1);;
00427 }
00428 
00429 QRect Applet::mapToView(const QGraphicsView *view, const QRectF &rect) const
00430 {
00431     // Why is this adjustment needed? Qt calculation error?
00432     return view->mapFromScene(mapToScene(rect)).boundingRect().adjusted(0, 0, -1, -1);;
00433 }
00434 
00435 QPoint Applet::popupPosition(const QSize &s) const
00436 {
00437     QGraphicsView *v = view();
00438     Q_ASSERT(v);
00439 
00440     QPoint pos = v->mapFromScene(scenePos());
00441     pos = v->mapToGlobal(pos);
00442     //kDebug() << "==> position is" << scenePos() << v->mapFromScene(scenePos()) << pos;
00443     Plasma::View *pv = dynamic_cast<Plasma::View *>(v);
00444 
00445     Plasma::Location loc = Floating;
00446     if (pv) {
00447         loc = pv->containment()->location();
00448     }
00449 
00450     switch (loc) {
00451     case BottomEdge:
00452         pos = QPoint(pos.x(), pos.y() - s.height());
00453         break;
00454     case TopEdge:
00455         pos = QPoint(pos.x(), pos.y() + (int)size().height());
00456         break;
00457     case LeftEdge:
00458         pos = QPoint(pos.x() + (int)size().width(), pos.y());
00459         break;
00460     case RightEdge:
00461         pos = QPoint(pos.x() - s.width(), pos.y());
00462         break;
00463     default:
00464         if (pos.y() - s.height() > 0) {
00465              pos = QPoint(pos.x(), pos.y() - s.height());
00466         } else {
00467              pos = QPoint(pos.x(), pos.y() + (int)size().height());
00468         }
00469     }
00470 
00471     //are we out of screen?
00472 
00473     QRect screenRect = QApplication::desktop()->screenGeometry(pv ? pv->containment()->screen() : -1);
00474     //kDebug() << "==> rect for" << (pv ? pv->containment()->screen() : -1) << "is" << screenRect;
00475 
00476     if (pos.rx() + s.width() > screenRect.right()) {
00477         pos.rx() -= ((pos.rx() + s.width()) - screenRect.right());
00478     }
00479 
00480     if (pos.ry() + s.height() > screenRect.bottom()) {
00481         pos.ry() -= ((pos.ry() + s.height()) - screenRect.bottom());
00482     }
00483     pos.rx() = qMax(0, pos.rx());
00484 
00485     return pos;
00486 }
00487 
00488 void Applet::updateConstraints(Plasma::Constraints constraints)
00489 {
00490     d->scheduleConstraintsUpdate(constraints);
00491 }
00492 
00493 void Applet::constraintsEvent(Plasma::Constraints constraints)
00494 {
00495     //NOTE: do NOT put any code in here that reacts to constraints updates
00496     //      as it will not get called for any applet that reimplements constraintsEvent
00497     //      without calling the Applet:: version as well, which it shouldn't need to.
00498     //      INSTEAD put such code into flushPendingConstraintsEvents
00499     Q_UNUSED(constraints)
00500     //kDebug() << constraints << "constraints are FormFactor: " << formFactor() << ", Location: " << location();
00501     if (d->script) {
00502         d->script->constraintsEvent(constraints);
00503     }
00504 }
00505 
00506 QString Applet::name() const
00507 {
00508     if (!d->appletDescription.isValid()) {
00509         return i18n("Unknown Applet");
00510     }
00511 
00512     return d->appletDescription.name();
00513 }
00514 
00515 QFont Applet::font() const
00516 {
00517     return QApplication::font();
00518 }
00519 
00520 QString Applet::icon() const
00521 {
00522     if (!d->appletDescription.isValid()) {
00523         return QString();
00524     }
00525 
00526     return d->appletDescription.icon();
00527 }
00528 
00529 QString Applet::pluginName() const
00530 {
00531     if (!d->appletDescription.isValid()) {
00532         return QString();
00533     }
00534 
00535     return d->appletDescription.pluginName();
00536 }
00537 
00538 bool Applet::shouldConserveResources() const
00539 {
00540     return Solid::PowerManagement::appShouldConserveResources();
00541 }
00542 
00543 QString Applet::category() const
00544 {
00545     if (!d->appletDescription.isValid()) {
00546         return i18n("Miscellaneous");
00547     }
00548 
00549     return d->appletDescription.category();
00550 }
00551 
00552 QString Applet::category(const KPluginInfo& applet)
00553 {
00554     return applet.property("X-KDE-PluginInfo-Category").toString();
00555 }
00556 
00557 QString Applet::category(const QString& appletName)
00558 {
00559     if (appletName.isEmpty()) {
00560         return QString();
00561     }
00562 
00563     QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
00564     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
00565 
00566     if (offers.isEmpty()) {
00567         return QString();
00568     }
00569 
00570     return offers.first()->property("X-KDE-PluginInfo-Category").toString();
00571 }
00572 
00573 ImmutabilityType Applet::immutability() const
00574 {
00575     //Returning the more strict immutability between the applet immutability and Corona one
00576     ImmutabilityType coronaImmutability = Mutable;
00577 
00578     if (dynamic_cast<Corona*>(scene())) {
00579         coronaImmutability = static_cast<Corona*>(scene())->immutability();
00580     }
00581 
00582     if (coronaImmutability == SystemImmutable) {
00583         return SystemImmutable;
00584     } else if (coronaImmutability == UserImmutable && d->immutability != SystemImmutable) {
00585         return UserImmutable;
00586     } else {
00587         return d->immutability;
00588     }
00589 }
00590 
00591 void Applet::setImmutability(const ImmutabilityType immutable)
00592 {
00593     if (d->immutability == immutable) {
00594         return;
00595     }
00596 
00597     d->immutability = immutable;
00598     updateConstraints(ImmutableConstraint);
00599 }
00600 
00601 Applet::BackgroundHints Applet::backgroundHints() const
00602 {
00603     return d->backgroundHints;
00604 }
00605 
00606 void Applet::setBackgroundHints(const BackgroundHints hints)
00607 {
00608     d->backgroundHints = hints;
00609 
00610     //Draw the standard background?
00611     if ((hints & StandardBackground) || (hints & TranslucentBackground)) {
00612         if (!d->background) {
00613             d->background = new Plasma::PanelSvg();
00614         }
00615 
00616         if ((hints & TranslucentBackground) && Plasma::Theme::defaultTheme()->currentThemeHasImage("widgets/translucentbackground")) {
00617             d->background->setImagePath("widgets/translucentbackground");
00618         } else {
00619             d->background->setImagePath("widgets/background");
00620         }
00621 
00622         d->background->setEnabledBorders(Plasma::PanelSvg::AllBorders);
00623         qreal left, top, right, bottom;
00624         d->background->getMargins(left, top, right, bottom);
00625         setContentsMargins(left, right, top, bottom);
00626         QSizeF fitSize(left + right, top + bottom);
00627         if (minimumSize().expandedTo(fitSize) != minimumSize()) {
00628             setMinimumSize(minimumSize().expandedTo(fitSize));
00629         }
00630         d->background->resizePanel(boundingRect().size());
00631     } else if (d->background) {
00632         qreal left, top, right, bottom;
00633         d->background->getMargins(left, top, right, bottom);
00634         //Setting a minimum size of 0,0 would result in the panel to be only
00635         //on the first virtual desktop
00636         setMinimumSize(qMax(minimumSize().width() - left - right, qreal(1.0)),
00637                        qMax(minimumSize().height() - top - bottom, qreal(1.0)));
00638 
00639         delete d->background;
00640         d->background = 0;
00641         setContentsMargins(0, 0, 0, 0);
00642     }
00643 
00644     //Draw the shadow?
00645     //There are various problems with shadows right now:
00646     //
00647     //1) shadows can be seen through translucent areas, which is probably technically correct ubt
00648     //looks odd
00649     //2) the shape of the item odesn't conform to the shape of the standard background, e.g. with
00650     //rounded corners
00651 #ifdef DYNAMIC_SHADOWS
00652     if (hints & ShadowedBackground) {
00653         if (d->shadow) {
00654             d->shadow->setVisible(true);
00655         } else {
00656             d->shadow = new ShadowItem(this);
00657             if (scene()) {
00658                 scene()->addItem(d->shadow);
00659                 d->shadow->show();
00660             }
00661         }
00662     } else {
00663         delete d->shadow;
00664         d->shadow = 0;
00665     }
00666 #endif
00667 }
00668 
00669 bool Applet::hasFailedToLaunch() const
00670 {
00671     return d->failed;
00672 }
00673 
00674 void Applet::paintWindowFrame(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
00675 {
00676     //Here come the code for the window frame
00677     //kDebug() << windowFrameGeometry();
00678     //painter->drawRoundedRect(windowFrameGeometry(), 5, 5);
00679 }
00680 
00681 bool Applet::configurationRequired() const
00682 {
00683     return d->needsConfigOverlay != 0;
00684 }
00685 
00686 void Applet::setConfigurationRequired(bool needsConfig, const QString &reason)
00687 {
00688     if ((d->needsConfigOverlay != 0) == needsConfig) {
00689         return;
00690     }
00691 
00692     if (d->needsConfigOverlay) {
00693         delete d->needsConfigOverlay;
00694         d->needsConfigOverlay = 0;
00695         return;
00696     }
00697 
00698     d->needsConfigOverlay = new AppletOverlayWidget(this);
00699     d->needsConfigOverlay->resize(contentsRect().size());
00700     d->needsConfigOverlay->setPos(contentsRect().topLeft());
00701 
00702     int zValue = 100;
00703     foreach (QGraphicsItem *child, QGraphicsItem::children()) {
00704         if (child->zValue() > zValue) {
00705             zValue = child->zValue() + 1;
00706         }
00707     }
00708     d->needsConfigOverlay->setZValue(zValue);
00709 
00710     qDeleteAll(d->needsConfigOverlay->QGraphicsItem::children());
00711     QGraphicsGridLayout *configLayout = new QGraphicsGridLayout(d->needsConfigOverlay);
00712     configLayout->setContentsMargins(0, 0, 0, 0);
00713 
00714   //  configLayout->addStretch();
00715     configLayout->setColumnStretchFactor(0, 10);
00716     configLayout->setColumnStretchFactor(2, 10);
00717     configLayout->setRowStretchFactor(0, 10);
00718     configLayout->setRowStretchFactor(3, 10);
00719 
00720     int row = 1;
00721     if (!reason.isEmpty()) {
00722         Label *explanation = new Label(d->needsConfigOverlay);
00723         explanation->setText(reason);
00724         configLayout->addItem(explanation, row, 1);
00725         configLayout->setColumnStretchFactor(1, 10);
00726         ++row;
00727         //configLayout->setAlignment(explanation, Qt::AlignBottom | Qt::AlignCenter);
00728     }
00729 
00730     PushButton *configWidget = new PushButton(d->needsConfigOverlay);
00731     configWidget->setText(i18n("Configure..."));
00732     connect(configWidget, SIGNAL(clicked()), this, SLOT(showConfigurationInterface()));
00733     configLayout->addItem(configWidget, row, 1);
00734     //configLayout->setAlignment(configWidget, Qt::AlignTop | Qt::AlignCenter);
00735     //configLayout->addStretch();
00736 
00737     d->needsConfigOverlay->show();
00738 }
00739 
00740 void Applet::flushPendingConstraintsEvents()
00741 {
00742     if (d->pendingConstraints == NoConstraint) {
00743         return;
00744     }
00745 
00746     if (d->constraintsTimerId) {
00747         killTimer(d->constraintsTimerId);
00748         d->constraintsTimerId = 0;
00749     }
00750 
00751     //kDebug() << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
00752     Plasma::Constraints c = d->pendingConstraints;
00753     d->pendingConstraints = NoConstraint;
00754 
00755     if (c & Plasma::StartupCompletedConstraint) {
00756         //common actions
00757         bool unlocked = immutability() == Mutable;
00758         //FIXME desktop containments can't be removed while in use.
00759         //it's kinda silly to have a keyboard shortcut for something that can only be used when the
00760         //shortcut isn't active.
00761         QAction* closeApplet = new QAction(i18n("Remove this %1", name()), this);
00762         closeApplet->setIcon(KIcon("edit-delete"));
00763         closeApplet->setEnabled(unlocked);
00764         closeApplet->setVisible(unlocked);
00765         closeApplet->setShortcutContext(Qt::WidgetWithChildrenShortcut); //don't clash with other views
00766         if (isContainment()) {
00767             closeApplet->setShortcut(QKeySequence("ctrl+shift+r"));
00768         } else {
00769             closeApplet->setShortcut(QKeySequence("ctrl+r"));
00770         }
00771         connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(selectItemToDestroy()));
00772         d->actions.addAction("remove", closeApplet);
00773     }
00774 
00775     if (c & Plasma::ImmutableConstraint) {
00776         bool unlocked = immutability() == Mutable;
00777         QAction *action = d->actions.action("remove");
00778         if (action) {
00779             action->setVisible(unlocked);
00780             action->setEnabled(unlocked);
00781         }
00782     }
00783 
00784     if (c & Plasma::SizeConstraint && d->needsConfigOverlay) {
00785         d->needsConfigOverlay->setGeometry(QRectF(QPointF(0, 0), contentsRect().size()));
00786         QGraphicsItem *button = 0;
00787         QList<QGraphicsItem*> children = d->needsConfigOverlay->QGraphicsItem::children();
00788 
00789         if (!children.isEmpty()) {
00790             button = children.first();
00791         }
00792 
00793         if (button) {
00794             QSizeF s = button->boundingRect().size();
00795             button->setPos(d->needsConfigOverlay->boundingRect().width() / 2 - s.width() / 2,
00796                            d->needsConfigOverlay->boundingRect().height() / 2 - s.height() / 2);
00797         }
00798     }
00799 
00800     if (c & Plasma::FormFactorConstraint) {
00801         FormFactor f = formFactor();
00802         //FIXME: this is a bit of a mess: calling setBackgroundHints twice,
00803         //       and moving an item to a non-vert/horiz containment that
00804         //       normally doesn't request a background would then get one!
00805         if (f == Planar) {
00806             setBackgroundHints(d->backgroundHints|ShadowedBackground);
00807         } else if (d->backgroundHints&ShadowedBackground) {
00808             setBackgroundHints(d->backgroundHints^ShadowedBackground);
00809         }
00810 
00811         if (!isContainment() && f != Vertical && f != Horizontal) {
00812             setBackgroundHints(d->backgroundHints|StandardBackground);
00813         } else if(d->backgroundHints&StandardBackground) {
00814             setBackgroundHints(d->backgroundHints^StandardBackground);
00815         } else if(d->backgroundHints&TranslucentBackground) {
00816             setBackgroundHints(d->backgroundHints^TranslucentBackground);
00817         }
00818     }
00819 
00820     //enforce square size in panels
00821     if ((c & Plasma::SizeConstraint || c & Plasma::FormFactorConstraint) &&
00822         aspectRatioMode() == Plasma::Square) {
00823         if (formFactor() == Horizontal) {
00824             setSizePolicy(QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Expanding));
00825         } else if (formFactor() == Vertical) {
00826             setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed));
00827         }
00828 
00829         updateGeometry();
00830     }
00831 
00832     //enforce a constrained square size in panels
00833     if ((c & Plasma::SizeConstraint || c & Plasma::FormFactorConstraint) &&
00834         aspectRatioMode() == Plasma::ConstrainedSquare) {
00835         if (formFactor() == Horizontal) {
00836             setSizePolicy(QSizePolicy(QSizePolicy::Maximum,QSizePolicy::Expanding));
00837         } else if (formFactor() == Vertical) {
00838             setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed));
00839         }
00840 
00841         updateGeometry();
00842     }
00843 
00844 
00845     Containment* containment = qobject_cast<Plasma::Containment*>(this);
00846     if (isContainment() && containment) {
00847         containment->d->containmentConstraintsEvent(c);
00848     }
00849 
00850     constraintsEvent(c);
00851 
00852     if (layout()) {
00853         layout()->updateGeometry();
00854     }
00855 }
00856 
00857 int Applet::type() const
00858 {
00859     return Type;
00860 }
00861 
00862 QList<QAction*> Applet::contextualActions()
00863 {
00864     //kDebug() << "empty context actions";
00865     return d->script ? d->script->contextualActions() : QList<QAction*>();
00866 }
00867 
00868 QAction* Applet::action(QString name) const
00869 {
00870     return d->actions.action(name);
00871 }
00872 
00873 void Applet::addAction(QString name, QAction *action)
00874 {
00875     d->actions.addAction(name, action);
00876 }
00877 
00878 void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
00879 {
00880     QPainter *p;
00881     //FIXME: we should probably set the pixmap to screenSize(), but that breaks stuff atm.
00882     QPixmap pixmap(boundingRect().size().toSize());
00883 
00884     QGraphicsView* qgv = qobject_cast<QGraphicsView*>(widget ? widget->parent() : 0);
00885     bool ghost = (qgv && (qgv == d->ghostView));
00886 
00887     if (ghost) {
00888         // The applet has to be displayed semi transparent. Create a pixmap and a painter on
00889         // that pixmap where the applet can draw on so we can draw the result transparently
00890         // at the end.
00891         kDebug() << "Painting ghosted...";
00892 
00893         pixmap.fill(Qt::transparent);
00894 
00895         p = new QPainter();
00896         p->begin(&pixmap);
00897     } else {
00898         p = painter;
00899     }
00900 
00901     if (d->shadow && d->shadow->shadowedSize() != boundingRect().size()) {
00902         //kDebug() << "sizes are " << d->shadow->shadowedSize() << boundingRect().size();
00903         d->shadow->generate();
00904     }
00905 
00906     p->save();
00907 
00908     if (transform().isRotating()) {
00909         p->setRenderHint(QPainter::SmoothPixmapTransform);
00910         p->setRenderHint(QPainter::Antialiasing);
00911     }
00912 
00913     if (d->background &&
00914         formFactor() != Plasma::Vertical &&
00915         formFactor() != Plasma::Horizontal) {
00916         //kDebug() << "option rect is" << option->rect;
00917         d->background->paintPanel(p, option->rect, QPointF(0,0));
00918     }
00919 
00920     if (!d->failed) {
00921         qreal left, top, right, bottom;
00922         getContentsMargins(&left, &top, &right, &bottom);
00923         const QRect contentsRect = QRectF(QPointF(0,0), boundingRect().size())
00924                         .adjusted(left, top, -right, -bottom).toAlignedRect();
00925         if (widget && isContainment()) {
00926             // note that the widget we get is actually the viewport of the view, not the view itself
00927             View* v = qobject_cast<Plasma::View*>(widget->parent());
00928             if (!v || v->isWallpaperEnabled()) {
00929                 Containment::StyleOption coption(*option);
00930                 coption.view = v;
00931 
00932                 paintInterface(p, &coption, contentsRect);
00933             }
00934 
00935             p->restore();
00936             return;
00937         }
00938 
00939         //kDebug() << "paint interface of" << (QObject*) this;
00940         paintInterface(p, option, contentsRect);
00941     }
00942     p->restore();
00943 
00944     if (ghost) {
00945         // Lets display the pixmap that we've just drawn... transparently.
00946         p->setCompositionMode(QPainter::CompositionMode_DestinationIn);
00947         p->fillRect(pixmap.rect(), QColor(0, 0, 0, (0.3 * 255)));
00948         p->end();
00949 
00950         delete p;
00951 
00952         painter->drawPixmap(0, 0, pixmap);
00953     }
00954 }
00955 
00956 void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option,
00957                             const QRect & contentsRect)
00958 {
00959     if (d->script) {
00960         d->script->paintInterface(painter, option, contentsRect);
00961     } else {
00962         //kDebug() << "Applet::paintInterface() default impl";
00963     }
00964 }
00965 
00966 FormFactor Applet::formFactor() const
00967 {
00968     Containment* c = containment();
00969     return c ? c->d->formFactor : Plasma::Planar;
00970 }
00971 
00972 Containment* Applet::containment() const
00973 {
00974     if (isContainment()) {
00975         Containment *c = dynamic_cast<Containment*>(const_cast<Applet*>(this));
00976         if (c) {
00977             return c;
00978         }
00979     }
00980 
00981     QGraphicsItem *parent = parentItem();
00982     Containment *c = 0;
00983 
00984     while (parent) {
00985         Containment *possibleC = dynamic_cast<Containment*>(parent);
00986         if (possibleC && possibleC->isContainment()) {
00987             c = possibleC;
00988             break;
00989         }
00990         parent = parent->parentItem();
00991     }
00992 
00993     return c;
00994 }
00995 
00996 void Applet::setGlobalShortcut(const KShortcut &shortcut)
00997 {
00998     if (!d->activationAction) {
00999         d->activationAction = new KAction(this);
01000         //TODO: add better text when we aren't in a string freeze
01001         d->activationAction->setText(name());
01002         d->activationAction->setObjectName(QString("Activate %1 Widget").arg(name())); // NO I18N
01003         connect(d->activationAction, SIGNAL(triggered()), this, SIGNAL(activate()));
01004         connect(this, SIGNAL(activate()), this, SLOT(setFocus()));
01005 
01006         QList<QWidget *> widgets = d->actions.associatedWidgets();
01007         foreach (QWidget *w, widgets) {
01008             w->addAction(d->activationAction);
01009         }
01010     }
01011 
01012     d->activationAction->setGlobalShortcut(shortcut);
01013 }
01014 
01015 KShortcut Applet::globalShortcut() const
01016 {
01017     if (d->activationAction) {
01018         return d->activationAction->globalShortcut();
01019     }
01020 
01021     return KShortcut();
01022 }
01023 
01024 void Applet::addAssociatedWidget(QWidget *widget)
01025 {
01026     d->actions.addAssociatedWidget(widget);
01027 }
01028 
01029 void Applet::removeAssociatedWidget(QWidget *widget)
01030 {
01031     d->actions.removeAssociatedWidget(widget);
01032 }
01033 
01034 Location Applet::location() const
01035 {
01036     Containment* c = containment();
01037     return c ? c->d->location : Plasma::Desktop;
01038 }
01039 
01040 Plasma::AspectRatioMode Applet::aspectRatioMode() const
01041 {
01042     return d->aspectRatioMode;
01043 }
01044 
01045 void Applet::setAspectRatioMode(Plasma::AspectRatioMode mode)
01046 {
01047     d->aspectRatioMode = mode;
01048 }
01049 
01050 void Applet::registerAsDragHandle( QGraphicsItem * item )
01051 {
01052     if (!item) {
01053         return;
01054     }
01055 
01056     int index = d->registeredAsDragHandle.indexOf(item);
01057 
01058     if (index == -1) {
01059         d->registeredAsDragHandle.append(item);
01060         item->installSceneEventFilter(this);
01061     }
01062 }
01063 
01064 void Applet::unregisterAsDragHandle(QGraphicsItem *item)
01065 {
01066     if (!item) {
01067         return;
01068     }
01069 
01070     int index = d->registeredAsDragHandle.indexOf(item);
01071     if (index != -1) {
01072         d->registeredAsDragHandle.removeAt(index);
01073         item->removeSceneEventFilter(this);
01074     }
01075 }
01076 
01077 bool Applet::isRegisteredAsDragHandle( QGraphicsItem * item )
01078 {
01079     return (d->registeredAsDragHandle.indexOf(item) != -1);
01080 }
01081 
01082 bool Applet::hasConfigurationInterface() const
01083 {
01084     return d->hasConfigurationInterface;
01085 }
01086 
01087 void Applet::setHasConfigurationInterface(bool hasInterface)
01088 {
01089     if (d->hasConfigurationInterface == hasInterface) {
01090         return;
01091     }
01092     d->hasConfigurationInterface = hasInterface;
01093     //config action
01094     //TODO respect security when it's implemented (4.2)
01095     QAction *configAction = d->actions.action("configure");
01096     if (hasInterface) {
01097         if (! configAction) { //should be always true
01098             configAction = new QAction(i18n("%1 Settings", name()), this);
01099             configAction->setIcon(KIcon("configure"));
01100             configAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); //don't clash with other views
01101             if (isContainment()) {
01102                 //kDebug() << "I am a containment";
01103                 configAction->setShortcut(QKeySequence("ctrl+shift+s"));
01104             } else {
01105                 configAction->setShortcut(QKeySequence("ctrl+s"));
01106             }
01107             //TODO how can we handle configuration of the shortcut in a way that spans all applets?
01108             connect(configAction, SIGNAL(triggered(bool)),
01109                     this, SLOT(showConfigurationInterface()));
01110             d->actions.addAction("configure", configAction);
01111         }
01112     } else {
01113         d->actions.removeAction(configAction);
01114     }
01115 }
01116 
01117 bool Applet::eventFilter( QObject *o, QEvent * e )
01118 {
01119     return QObject::eventFilter(o, e);
01120 }
01121 
01122 bool Applet::sceneEventFilter( QGraphicsItem * watched, QEvent * event )
01123 {
01124     switch (event->type()) {
01125         case QEvent::GraphicsSceneMouseMove: {
01126             //don't move when the containment is not mutable, in the rare case the containment doesn't exists consider it as mutable
01127             if ((!containment() || containment()->immutability() == Mutable) && d->registeredAsDragHandle.contains( watched )) {
01128                 mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event));
01129                 return true;
01130             }
01131             break;
01132         }
01133 
01134         default:
01135             break;
01136     }
01137 
01138     return QGraphicsItem::sceneEventFilter(watched, event);
01139 }
01140 
01141 void Applet::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
01142 {
01143     if (immutability() == Mutable && formFactor() == Plasma::Planar) {
01144         QGraphicsItem *parent = parentItem();
01145         Plasma::Applet *applet = qgraphicsitem_cast<Plasma::Applet*>(parent);
01146 
01147         if (applet && applet->isContainment()) {
01148             // our direct parent is a containment. just move ourselves.
01149             QPointF curPos = event->pos();
01150             QPointF lastPos = event->lastPos();
01151             QPointF delta = curPos-lastPos;
01152 
01153             moveBy(delta.x(),delta.y());
01154         } else if (parent) {
01155             //don't move the icon as well because our parent (usually an appletHandle) will do it for us
01156             //parent->moveBy(delta.x(),delta.y());
01157             QPointF curPos = parent->transform().map(event->pos());
01158             QPointF lastPos = parent->transform().map(event->lastPos());
01159             QPointF delta = curPos-lastPos;
01160 
01161             parent->setPos(parent->pos() + delta);
01162         }
01163     }
01164 }
01165 
01166 void Applet::mousePressEvent(QGraphicsSceneMouseEvent *event)
01167 {
01168     setFocus(Qt::MouseFocusReason);
01169     QGraphicsWidget::mousePressEvent(event);
01170 }
01171 
01172 void Applet::focusInEvent(QFocusEvent * event)
01173 {
01174     if (!isContainment() && containment()) {
01175         //focusing an applet may trigger this event again, but we won't be here more than twice
01176         containment()->d->focusApplet(this);
01177     }
01178 
01179     QGraphicsWidget::focusInEvent(event);
01180 }
01181 
01182 void Applet::resizeEvent(QGraphicsSceneResizeEvent *event)
01183 {
01184     QGraphicsWidget::resizeEvent(event);
01185 
01186     if (d->background) {
01187         d->background->resizePanel(boundingRect().size());
01188     }
01189 
01190     updateConstraints(Plasma::SizeConstraint);
01191     emit geometryChanged();
01192 }
01193 
01194 void Applet::showConfigurationInterface()
01195 {
01196     if (!hasConfigurationInterface()) {
01197         return;
01198     }
01199 
01200     const QString dialogId = QString("%1settings%2").arg(id()).arg(name());
01201     KConfigDialog * dlg = KConfigDialog::exists(dialogId);
01202 
01203     if (dlg) {
01204         KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop());
01205         dlg->show();
01206         KWindowSystem::activateWindow(dlg->winId());
01207         return;
01208     }
01209 
01210     const QString windowTitle = i18nc("@title:window", "%1 Settings", name());
01211     if (d->package && d->configXml) {
01212         QString uiFile = d->package->filePath("mainconfigui");
01213         if (uiFile.isEmpty()) {
01214             return;
01215         }
01216 
01217         KConfigDialog *dialog = new KConfigDialog(0, dialogId, d->configXml);
01218         dialog->setWindowTitle(windowTitle);
01219         dialog->setAttribute(Qt::WA_DeleteOnClose, true);
01220 
01221         QUiLoader loader;
01222         QFile f(uiFile);
01223         if (!f.open(QIODevice::ReadOnly)) {
01224             delete dialog;
01225 
01226             if (d->script) {
01227                 d->script->showConfigurationInterface();
01228             }
01229             return;
01230         }
01231 
01232         QWidget *w = loader.load(&f);
01233         f.close();
01234 
01235         dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name()));
01236         dialog->show();
01237     } else if (d->script) {
01238         d->script->showConfigurationInterface();
01239     } else {
01240         KConfigSkeleton *nullManager = new KConfigSkeleton(0);
01241         KConfigDialog *dialog = new KConfigDialog(0, dialogId, nullManager);
01242         dialog->setFaceType(KPageDialog::Auto);
01243         dialog->setWindowTitle(windowTitle);
01244         dialog->setAttribute(Qt::WA_DeleteOnClose, true);
01245         createConfigurationInterface(dialog);
01246         //TODO: would be nice to not show dialog if there are no pages added?
01247         connect(dialog, SIGNAL(finished()), nullManager, SLOT(deleteLater()));
01248         //TODO: Apply button does not correctly work for now, so do not show it
01249         dialog->showButton( KDialog::Apply, false );
01250         dialog->show();
01251     }
01252 
01253     emit releaseVisualFocus();
01254 }
01255 
01256 void Applet::createConfigurationInterface(KConfigDialog *parent)
01257 {
01258     Q_UNUSED(parent)
01259     // virtual method reimplemented by subclasses.
01260     // do not put anything here ...
01261 }
01262 
01263 KPluginInfo::List Applet::listAppletInfo(const QString &category,
01264                                        const QString &parentApp)
01265 {
01266     QString constraint;
01267 
01268     if (parentApp.isEmpty()) {
01269         constraint.append("not exist [X-KDE-ParentApp]");
01270     } else {
01271         constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
01272     }
01273 
01274     if (!category.isEmpty()) {
01275         if (!constraint.isEmpty()) {
01276             constraint.append(" and ");
01277         }
01278 
01279         constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
01280         if (category == "Miscellaneous") {
01281             constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
01282         }
01283     }
01284 
01285     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01286     //kDebug() << "Applet::listAppletInfo constraint was '" << constraint << "' which got us " << offers.count() << " matches";
01287     return KPluginInfo::fromServices(offers);
01288 }
01289 
01290 KPluginInfo::List Applet::listAppletInfoForMimetype(const QString &mimetype)
01291 {
01292     QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
01293     //kDebug() << "listAppletInfoForMimetype with" << mimetype << constraint;
01294     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01295     return KPluginInfo::fromServices(offers);
01296 }
01297 
01298 QStringList Applet::listCategories(const QString &parentApp, bool visibleOnly)
01299 {
01300     QString constraint = "exist [X-KDE-PluginInfo-Category]";
01301 
01302     if (parentApp.isEmpty()) {
01303         constraint.append(" and not exist [X-KDE-ParentApp]");
01304     } else {
01305         constraint.append(" and [X-KDE-ParentApp] == '").append(parentApp).append("'");
01306     }
01307 
01308     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01309     QStringList categories;
01310     foreach (const KService::Ptr &applet, offers) {
01311         QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString();
01312         if (visibleOnly && applet->noDisplay()) {
01313             // we don't want to show the hidden category
01314             continue;
01315         }
01316 
01317         //kDebug() << "   and we have " << appletCategory;
01318         if (appletCategory.isEmpty()) {
01319             if (!categories.contains(i18n("Miscellaneous"))) {
01320                 categories << i18n("Miscellaneous");
01321             }
01322         } else  if (!categories.contains(appletCategory)) {
01323             categories << appletCategory;
01324         }
01325     }
01326 
01327     categories.sort();
01328     return categories;
01329 }
01330 
01331 Applet* Applet::load(const QString& appletName, uint appletId, const QVariantList& args)
01332 {
01333     if (appletName.isEmpty()) {
01334         return 0;
01335     }
01336 
01337     QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
01338     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01339 
01340     bool isContainment = false;
01341     if (offers.isEmpty()) {
01342         //TODO: what would be -really- cool is offer to try and download the applet
01343         //      from the network at this point
01344         offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
01345         isContainment = true;
01346         if (offers.isEmpty()) {
01347             kDebug() << "offers is empty for " << appletName;
01348             return 0;
01349         }
01350     } /* else if (offers.count() > 1) {
01351         kDebug() << "hey! we got more than one! let's blindly take the first one";
01352     } */
01353 
01354     KService::Ptr offer = offers.first();
01355 
01356     if (appletId == 0) {
01357         appletId = ++AppletPrivate::s_maxAppletId;
01358     }
01359 
01360     if (!offer->property("X-Plasma-API").toString().isEmpty()) {
01361         kDebug() << "we have a script using the" << offer->property("X-Plasma-API").toString() << "API";
01362         if (isContainment) {
01363             return new Containment(0, offer->storageId(), appletId);
01364         }
01365         return new Applet(0, offer->storageId(), appletId);
01366     }
01367 
01368     KPluginLoader plugin(*offer);
01369 
01370     if (!Plasma::isPluginVersionCompatible(plugin.pluginVersion())) {
01371         return 0;
01372     }
01373 
01374     QVariantList allArgs;
01375     allArgs << offer->storageId() << appletId << args;
01376     QString error;
01377     Applet* applet = offer->createInstance<Plasma::Applet>(0, allArgs, &error);
01378 
01379     if (!applet) {
01380         kDebug() << "Couldn't load applet \"" << appletName << "\"! reason given: " << error;
01381     }
01382 
01383     return applet;
01384 }
01385 
01386 Applet* Applet::load(const KPluginInfo& info, uint appletId, const QVariantList& args)
01387 {
01388     if (!info.isValid()) {
01389         return 0;
01390     }
01391 
01392     return load(info.pluginName(), appletId, args);
01393 }
01394 
01395 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value)
01396 {
01397     //kDebug() << change;
01398     switch (change) {
01399     case ItemPositionChange:
01400         if (d->shadow) {
01401             d->shadow->adjustPosition();
01402         }
01403         break;
01404     case ItemSceneHasChanged: {
01405         QGraphicsScene *newScene = qvariant_cast<QGraphicsScene*>(value);
01406         if (newScene) {
01407             d->checkImmutability();
01408         }
01409 
01410         if (d->shadow) {
01411             if (d->shadow->scene()) {
01412                 d->shadow->scene()->removeItem(d->shadow);
01413             }
01414 
01415             if (newScene) {
01416                 newScene->addItem(d->shadow);
01417                 d->shadow->generate();
01418                 d->shadow->adjustPosition();
01419                 d->shadow->show();
01420             }
01421         }
01422     }
01423         break;
01424     case ItemVisibleChange:
01425         if (d->shadow) {
01426             d->shadow->setVisible(isVisible());
01427         }
01428         break;
01429     case ItemPositionHasChanged:
01430         emit geometryChanged();
01431         break;
01432     default:
01433         break;
01434     };
01435 
01436     return QGraphicsWidget::itemChange(change, value);
01437 }
01438 
01439 QPainterPath Applet::shape() const
01440 {
01441     if (d->script) {
01442         return d->script->shape();
01443     }
01444 
01445     return QGraphicsWidget::shape();
01446 }
01447 
01448 QSizeF Applet::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
01449 {
01450     QSizeF hint = QGraphicsWidget::sizeHint(which, constraint);
01451 
01452     //in panels make sure that the contents won't exit from the panel
01453     if (formFactor() == Horizontal && which == Qt::MinimumSize) {
01454         hint.setHeight(0);
01455     } else if (formFactor() == Vertical && which == Qt::MinimumSize) {
01456         hint.setWidth(0);
01457     }
01458 
01459     // enforce a square size in panels
01460     if (d->aspectRatioMode == Plasma::Square) {
01461         if (formFactor() == Horizontal) {
01462             hint.setWidth(size().height());
01463         } else if (formFactor() == Vertical) {
01464             hint.setHeight(size().width());
01465         }
01466     } else if (d->aspectRatioMode == Plasma::ConstrainedSquare) {
01467         //enforce a size not wider than tall
01468         if (formFactor() == Horizontal && (which == Qt::MaximumSize || size().height() <= KIconLoader::SizeLarge)) {
01469             hint.setWidth(size().height());
01470         //enforce a size not taller than wide
01471         } else if (formFactor() == Vertical && (which == Qt::MaximumSize || size().width() <= KIconLoader::SizeLarge)) {
01472             hint.setHeight(size().width());
01473         }
01474     }
01475 
01476     return hint;
01477 }
01478 
01479 void Applet::timerEvent(QTimerEvent *event)
01480 {
01481     if (event->timerId() == d->constraintsTimerId) {
01482         killTimer(d->constraintsTimerId);
01483         d->constraintsTimerId = 0;
01484         flushPendingConstraintsEvents();
01485     }
01486 }
01487 
01488 QRect Applet::screenRect() const
01489 {
01490     QPointF bottomRight = pos();
01491     bottomRight.setX(bottomRight.x() + size().width());
01492     bottomRight.setY(bottomRight.y() + size().height());
01493 
01494     Containment *c;
01495     c = containment();
01496 
01497     if (c) {
01498         QGraphicsView *v;
01499         v = c->view();
01500 
01501         if (v) {
01502             QPoint tL = v->mapToGlobal(v->mapFromScene(pos()));
01503             QPoint bR = v->mapToGlobal(v->mapFromScene(bottomRight));
01504 
01505             kDebug() << "screenRect = " << QPoint(tL.x(), tL.y()), QSize(bR.x() - tL.x(), bR.y() - tL.y());
01506             return QRect(QPoint(tL.x(), tL.y()), QSize(bR.x() - tL.x(), bR.y() - tL.y()));
01507         }
01508     }
01509 
01510     //The applet isn't in any containment, or in a containment that doesn't have a view on it.
01511     //So a screenRect isn't relevant.
01512     return QRect(QPoint(0, 0), QSize(0, 0));
01513 }
01514 
01515 void Applet::raise()
01516 {
01517     setZValue(++AppletPrivate::s_maxZValue);
01518 }
01519 
01520 void Applet::lower()
01521 {
01522     setZValue(--AppletPrivate::s_minZValue);
01523 }
01524 
01525 void Applet::setIsContainment(bool isContainment)
01526 {
01527     if (d->isContainment == isContainment) {
01528         return;
01529     }
01530 
01531     d->isContainment = isContainment;
01532 
01533     Containment *c = qobject_cast<Containment*>(this);
01534     if (c) {
01535         if (isContainment) {
01536             // set up the toolbox
01537             c->d->createToolBox();
01538         } else {
01539             delete c->d->toolBox;
01540             c->d->toolBox = 0;
01541         }
01542     }
01543 }
01544 
01545 bool Applet::isContainment() const
01546 {
01547     return d->isContainment;
01548 }
01549 
01550 
01551 // PRIVATE CLASS IMPLEMENTATION
01552 
01553 AppletPrivate::AppletPrivate(KService::Ptr service, int uniqueID, Applet *applet)
01554         : appletId(uniqueID),
01555           q(applet),
01556           backgroundHints(Applet::StandardBackground),
01557           appletDescription(service),
01558           package(0),
01559           needsConfigOverlay(0),
01560           background(0),
01561           script(0),
01562           configXml(0),
01563           shadow(0),
01564           mainConfig(0),
01565           pendingConstraints(NoConstraint),
01566           aspectRatioMode(Plasma::KeepAspectRatio),
01567           ghostView(0),
01568           immutability(Mutable),
01569           actions(applet),
01570           activationAction(0),
01571           constraintsTimerId(0),
01572           hasConfigurationInterface(false),
01573           failed(false),
01574           isContainment(false),
01575           transient(false)
01576 {
01577     if (appletId == 0) {
01578         appletId = ++s_maxAppletId;
01579     } else if (appletId > s_maxAppletId) {
01580         s_maxAppletId = appletId;
01581     }
01582 }
01583 
01584 AppletPrivate::~AppletPrivate()
01585 {
01586     foreach ( const QString& engine, loadedEngines ) {
01587         DataEngineManager::self()->unloadEngine( engine );
01588     }
01589     delete background;
01590     delete package;
01591     delete configXml;
01592     delete shadow;
01593     delete mainConfig;
01594 }
01595 
01596 void AppletPrivate::init()
01597 {
01598     // WARNING: do not access config() OR globalConfig() in this method!
01599     //          that requires a scene, which is not available at this point
01600     q->setCacheMode(Applet::DeviceCoordinateCache);
01601     q->setAcceptsHoverEvents(true);
01602     q->setFlag(QGraphicsItem::ItemIsFocusable, true);
01603     // FIXME: adding here because nothing seems to be doing it in QGraphicsView,
01604     // but it doesn't actually work anyways =/
01605     q->setLayoutDirection(qApp->layoutDirection());
01606 
01607     if (!appletDescription.isValid()) {
01608         kDebug() << "Check your constructor! You probably want to be passing in a Service::Ptr or a QVariantList with a valid storageid as arg[0].";
01609         return;
01610     }
01611 
01612     QString api = appletDescription.property("X-Plasma-API").toString();
01613 
01614     // we have a scripted plasmoid
01615     if (!api.isEmpty()) {
01616         // find where the Package is
01617         QString path = KStandardDirs::locate("data",
01618                                              "plasma/plasmoids/" + appletDescription.pluginName() + "/");
01619 
01620         if (path.isEmpty()) {
01621             q->setFailedToLaunch(true, i18n("Could not locate the %1 package required for the %2 widget.",
01622                                             appletDescription.pluginName(), appletDescription.name()));
01623         } else {
01624             // create the package and see if we have something real
01625             //kDebug() << "trying for" << path;
01626             PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::AppletComponent);
01627             structure->setPath(path);
01628             package = new Package(path, structure);
01629 
01630             if (package->isValid()) {
01631                 // now we try and set up the script engine.
01632                 // it will be parented to this applet and so will get
01633                 // deleted when the applet does
01634 
01635                 script = Plasma::loadScriptEngine(api, q);
01636                 if (!script) {
01637                     delete package;
01638                     package = 0;
01639                     q->setFailedToLaunch(true, i18n("Could not create a %1 ScriptEngine for the %2 widget.",
01640                                                     api, appletDescription.name()));
01641                 }
01642             } else {
01643                 q->setFailedToLaunch(true, i18n("Could not open the %1 package required for the %2 widget.",
01644                                                         appletDescription.pluginName(), appletDescription.name()));
01645                 delete package;
01646                 package = 0;
01647             }
01648 
01649             if (package) {
01650                 setupScriptSupport();
01651             }
01652         }
01653     }
01654 
01655     q->setBackgroundHints(Applet::DefaultBackground);
01656 
01657     QObject::connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), q, SLOT(themeChanged()));
01658 }
01659 
01660 // put all setup routines for script here. at this point we can assume that
01661 // package exists and that we have a script engin
01662 void AppletPrivate::setupScriptSupport()
01663 {
01664     Q_ASSERT(package);
01665     QString xmlPath = package->filePath("mainconfigxml");
01666     if (!xmlPath.isEmpty()) {
01667         QFile file(xmlPath);
01668         // FIXME: KConfigSkeleton doesn't play well with KConfigGroup =/
01669         KConfigGroup config = q->config();
01670         configXml = new ConfigXml(&config, &file);
01671     }
01672 
01673     if (!package->filePath("mainconfigui").isEmpty()) {
01674         q->setHasConfigurationInterface(true);
01675     }
01676 }
01677 
01678 QString AppletPrivate::globalName() const
01679 {
01680     if (!appletDescription.isValid()) {
01681         return QString();
01682     }
01683 
01684     return appletDescription.service()->library();
01685 }
01686 
01687 QString AppletPrivate::instanceName()
01688 {
01689     if (!appletDescription.isValid()) {
01690         return QString();
01691     }
01692 
01693     return appletDescription.service()->library() + QString::number(appletId);
01694 }
01695 
01696 void AppletPrivate::scheduleConstraintsUpdate(Plasma::Constraints c)
01697 {
01698     if (!constraintsTimerId) {
01699         constraintsTimerId = q->startTimer(0);
01700     }
01701     pendingConstraints |= c;
01702 }
01703 
01704 KConfigGroup* AppletPrivate::mainConfigGroup()
01705 {
01706     if (mainConfig) {
01707         return mainConfig;
01708     }
01709 
01710     if (isContainment) {
01711         const Containment *asContainment = qobject_cast<Containment*>(const_cast<Applet*>(q));
01712         Q_ASSERT(asContainment);
01713 
01714         KConfigGroup containmentConfig;
01715         //kDebug() << "got a corona, baby?" << (QObject*)asContainment->corona();
01716         if (asContainment->corona()) {
01717             containmentConfig = KConfigGroup(asContainment->corona()->config(), "Containments");
01718         } else {
01719             containmentConfig =  KConfigGroup(KGlobal::config(), "Containments");
01720         }
01721 
01722         mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId));
01723     } else {
01724         KConfigGroup appletConfig;
01725         if (q->containment()) {
01726             appletConfig = q->containment()->config();
01727             appletConfig = KConfigGroup(&appletConfig, "Applets");
01728         } else {
01729             kWarning() << "requesting config for" << q->name() << "without a containment!";
01730             appletConfig = KConfigGroup(KGlobal::config(), "Applets");
01731         }
01732 
01733         mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId));
01734     }
01735 
01736     return mainConfig;
01737 }
01738 
01739 QString AppletPrivate::visibleFailureText(const QString& reason)
01740 {
01741     QString text;
01742 
01743     if (reason.isEmpty()) {
01744         text = i18n("This object could not be created.");
01745     } else {
01746         text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason);
01747     }
01748 
01749     return text;
01750 }
01751 
01752 void AppletPrivate::checkImmutability()
01753 {
01754     const bool systemImmutable = q->globalConfig().isImmutable() || q->config().isImmutable() ||
01755                                 ((!isContainment && q->containment()) &&
01756                                     q->containment()->immutability() == SystemImmutable) ||
01757                                 (dynamic_cast<Corona*>(q->scene()) && static_cast<Corona*>(q->scene())->immutability() == SystemImmutable);
01758 
01759     if (systemImmutable) {
01760         q->updateConstraints(ImmutableConstraint);
01761     }
01762 }
01763 
01764 void AppletPrivate::themeChanged()
01765 {
01766     if (background) {
01767         //do again the translucent background fallback
01768         q->setBackgroundHints(backgroundHints);
01769 
01770         qreal left;
01771         qreal right;
01772         qreal top;
01773         qreal bottom;
01774         background->getMargins(left, top, right, bottom);
01775         q->setContentsMargins(left, right, top, bottom);
01776     }
01777     q->update();
01778 }
01779 
01780 void AppletPrivate::resetConfigurationObject()
01781 {
01782     mainConfigGroup()->deleteGroup();
01783     delete mainConfig;
01784     mainConfig = 0;
01785 }
01786 
01787 uint AppletPrivate::s_maxAppletId = 0;
01788 uint AppletPrivate::s_maxZValue = 0;
01789 uint AppletPrivate::s_minZValue = 0;
01790 PackageStructure::Ptr AppletPrivate::packageStructure(0);
01791 
01792 AppletOverlayWidget::AppletOverlayWidget(QGraphicsWidget *parent)
01793     : QGraphicsWidget(parent)
01794 {
01795     resize(parent->size());
01796 }
01797 
01798 void AppletOverlayWidget::paint(QPainter *painter,
01799                                 const QStyleOptionGraphicsItem *option,
01800                                 QWidget *widget)
01801 {
01802     Q_UNUSED(option)
01803     Q_UNUSED(widget)
01804     painter->save();
01805     painter->setRenderHint(QPainter::Antialiasing);
01806     QColor wash = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor);
01807     wash.setAlphaF(.6);
01808     painter->fillPath(parentItem()->shape(), wash);
01809     painter->restore();
01810 }
01811 
01812 } // Plasma namespace
01813 
01814 #include "applet.moc"

libplasma

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

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal