• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • KDE Home
  • Contact Us
 

Plasma

containment.cpp
Go to the documentation of this file.
00001 /*
00002  *   Copyright 2007 by Aaron Seigo <aseigo@kde.org>
00003  *   Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
00004  *   Copyright 2009 Chani Armitage <chani@kde.org>
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 "containment.h"
00023 #include "private/containment_p.h"
00024 
00025 #include "config-plasma.h"
00026 
00027 #include <QApplication>
00028 #include <QClipboard>
00029 #include <QFile>
00030 #include <QGraphicsSceneContextMenuEvent>
00031 #include <QGraphicsView>
00032 #include <QMimeData>
00033 #include <QPainter>
00034 #include <QStyleOptionGraphicsItem>
00035 #include <QGraphicsLayout>
00036 #include <QGraphicsLinearLayout>
00037 
00038 #include <kaction.h>
00039 #include <kauthorized.h>
00040 #include <kicon.h>
00041 #include <kmenu.h>
00042 #include <kmessagebox.h>
00043 #include <kmimetype.h>
00044 #include <kservicetypetrader.h>
00045 #include <kstandarddirs.h>
00046 #include <ktemporaryfile.h>
00047 #include <kwindowsystem.h>
00048 
00049 #ifndef PLASMA_NO_KIO
00050 #include "kio/jobclasses.h" // for KIO::JobFlags
00051 #include "kio/job.h"
00052 #include "kio/scheduler.h"
00053 #endif
00054 
00055 #include "abstracttoolbox.h"
00056 #include "animator.h"
00057 #include "context.h"
00058 #include "containmentactions.h"
00059 #include "containmentactionspluginsconfig.h"
00060 #include "corona.h"
00061 #include "extender.h"
00062 #include "extenderitem.h"
00063 #include "svg.h"
00064 #include "wallpaper.h"
00065 
00066 #include "remote/accessappletjob.h"
00067 #include "remote/accessmanager.h"
00068 
00069 #include "private/applet_p.h"
00070 #include "private/containmentactionspluginsconfig_p.h"
00071 #include "private/extenderitemmimedata_p.h"
00072 #include "private/extenderapplet_p.h"
00073 #include "private/wallpaper_p.h"
00074 
00075 #include "plasma/plasma.h"
00076 #include "animations/animation.h"
00077 
00078 namespace Plasma
00079 {
00080 
00081 bool ContainmentPrivate::s_positioningPanels = false;
00082 QHash<QString, ContainmentActions*> ContainmentPrivate::globalActionPlugins;
00083 static const char defaultWallpaper[] = "image";
00084 static const char defaultWallpaperMode[] = "SingleImage";
00085 
00086 Containment::StyleOption::StyleOption()
00087     : QStyleOptionGraphicsItem(),
00088       view(0)
00089 {
00090     version = Version;
00091     type = Type;
00092 }
00093 
00094 Containment::StyleOption::StyleOption(const Containment::StyleOption & other)
00095     : QStyleOptionGraphicsItem(other),
00096       view(other.view)
00097 {
00098     version = Version;
00099     type = Type;
00100 }
00101 
00102 Containment::StyleOption::StyleOption(const QStyleOptionGraphicsItem &other)
00103     : QStyleOptionGraphicsItem(other),
00104       view(0)
00105 {
00106     version = Version;
00107     type = Type;
00108 }
00109 
00110 Containment::Containment(QGraphicsItem *parent,
00111                          const QString &serviceId,
00112                          uint containmentId)
00113     : Applet(parent, serviceId, containmentId),
00114       d(new ContainmentPrivate(this))
00115 {
00116     // WARNING: do not access config() OR globalConfig() in this method!
00117     //          that requires a scene, which is not available at this point
00118     setPos(0, 0);
00119     setBackgroundHints(NoBackground);
00120     setContainmentType(CustomContainment);
00121     setHasConfigurationInterface(false);
00122 }
00123 
00124 Containment::Containment(QObject *parent, const QVariantList &args)
00125     : Applet(parent, args),
00126       d(new ContainmentPrivate(this))
00127 {
00128     // WARNING: do not access config() OR globalConfig() in this method!
00129     //          that requires a scene, which is not available at this point
00130     setPos(0, 0);
00131     setBackgroundHints(NoBackground);
00132     setHasConfigurationInterface(false);
00133 }
00134 
00135 Containment::Containment(const QString &packagePath, uint appletId, const QVariantList &args)
00136     : Plasma::Applet(packagePath, appletId, args),
00137       d(new ContainmentPrivate(this))
00138 {
00139     // WARNING: do not access config() OR globalConfig() in this method!
00140     //          that requires a scene, which is not available at this point
00141     setPos(0, 0);
00142     setBackgroundHints(NoBackground);
00143     setHasConfigurationInterface(false);
00144 }
00145 
00146 Containment::~Containment()
00147 {
00148     delete d;
00149     // Applet touches our dptr if we are a containment and is the superclass (think of dtors)
00150     // so we reset this as we exit the building
00151     Applet::d->isContainment = false;
00152 }
00153 
00154 void Containment::init()
00155 {
00156     Applet::init();
00157     if (!isContainment()) {
00158         return;
00159     }
00160 
00161     setCacheMode(NoCache);
00162     setFlag(QGraphicsItem::ItemIsMovable, false);
00163     setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
00164     setAcceptDrops(true);
00165     setAcceptsHoverEvents(true);
00166 
00167     if (d->type == NoContainmentType) {
00168         setContainmentType(DesktopContainment);
00169     }
00170 
00171     //connect actions
00172     ContainmentPrivate::addDefaultActions(d->actions(), this);
00173     bool unlocked = immutability() == Mutable;
00174 
00175     //fix the text of the actions that need name()
00176     //btw, do we really want to use name() when it's a desktopcontainment?
00177     QAction *closeApplet = action("remove");
00178     if (closeApplet) {
00179         closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", name()));
00180     }
00181 
00182     QAction *configAction = action("configure");
00183     if (configAction) {
00184         configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", name()));
00185     }
00186 
00187     QAction *appletBrowserAction = action("add widgets");
00188     if (appletBrowserAction) {
00189         appletBrowserAction->setVisible(unlocked);
00190         appletBrowserAction->setEnabled(unlocked);
00191         connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets()));
00192     }
00193 
00194     QAction *act = action("next applet");
00195     if (act) {
00196         connect(act, SIGNAL(triggered()), this, SLOT(focusNextApplet()));
00197     }
00198 
00199     act = action("previous applet");
00200     if (act) {
00201         connect(act, SIGNAL(triggered()), this, SLOT(focusPreviousApplet()));
00202     }
00203 
00204     if (immutability() != SystemImmutable && corona()) {
00205         QAction *lockDesktopAction = corona()->action("lock widgets");
00206         //keep a pointer so nobody notices it moved to corona
00207         if (lockDesktopAction) {
00208             d->actions()->addAction("lock widgets", lockDesktopAction);
00209         }
00210     }
00211     if (d->type != PanelContainment && d->type != CustomPanelContainment) {
00212         if (corona()) {
00213             //FIXME this is just here because of the darn keyboard shortcut :/
00214             act = corona()->action("manage activities");
00215             if (act) {
00216                 d->actions()->addAction("manage activities", act);
00217             }
00218             //a stupid hack to make this one's keyboard shortcut work
00219             act = corona()->action("configure shortcuts");
00220             if (act) {
00221                 d->actions()->addAction("configure shortcuts", act);
00222             }
00223         }
00224 
00225         if (d->type == DesktopContainment) {
00226             addToolBoxAction(action("add widgets"));
00227 
00228             //TODO: do we need some way to allow this be overridden?
00229             //      it's always available because shells rely on this
00230             //      to offer their own custom configuration as well
00231             QAction *configureContainment = action("configure");
00232             if (configureContainment) {
00233                 addToolBoxAction(configureContainment);
00234             }
00235         }
00236     }
00237 }
00238 
00239 void ContainmentPrivate::addDefaultActions(KActionCollection *actions, Containment *c)
00240 {
00241     actions->setConfigGroup("Shortcuts-Containment");
00242 
00243     //adjust applet actions
00244     KAction *appAction = qobject_cast<KAction*>(actions->action("remove"));
00245     appAction->setShortcut(KShortcut("alt+d, alt+r"));
00246     if (c && c->d->isPanelContainment()) {
00247         appAction->setText(i18n("Remove this Panel"));
00248     } else {
00249         appAction->setText(i18n("Remove this Activity"));
00250     }
00251 
00252     appAction = qobject_cast<KAction*>(actions->action("configure"));
00253     if (appAction) {
00254         appAction->setShortcut(KShortcut("alt+d, alt+s"));
00255         appAction->setText(i18n("Activity Settings"));
00256     }
00257 
00258     //add our own actions
00259     KAction *appletBrowserAction = actions->addAction("add widgets");
00260     appletBrowserAction->setAutoRepeat(false);
00261     appletBrowserAction->setText(i18n("Add Widgets..."));
00262     appletBrowserAction->setIcon(KIcon("list-add"));
00263     appletBrowserAction->setShortcut(KShortcut("alt+d, a"));
00264     appletBrowserAction->setData(AbstractToolBox::AddTool);
00265 
00266     KAction *action = actions->addAction("next applet");
00267     action->setText(i18n("Next Widget"));
00268     //no icon
00269     action->setShortcut(KShortcut("alt+d, n"));
00270     action->setData(AbstractToolBox::ControlTool);
00271 
00272     action = actions->addAction("previous applet");
00273     action->setText(i18n("Previous Widget"));
00274     //no icon
00275     action->setShortcut(KShortcut("alt+d, p"));
00276     action->setData(AbstractToolBox::ControlTool);
00277 }
00278 
00279 // helper function for sorting the list of applets
00280 bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2)
00281 {
00282     QPointF p1 = c1.readEntry("geometry", QRectF()).topLeft();
00283     QPointF p2 = c2.readEntry("geometry", QRectF()).topLeft();
00284 
00285     if (!qFuzzyCompare(p1.x(), p2.x())) {
00286         if (QApplication::layoutDirection() == Qt::RightToLeft) {
00287             return p1.x() > p2.x();
00288         }
00289 
00290         return p1.x() < p2.x();
00291     }
00292 
00293     return qFuzzyCompare(p1.y(), p2.y()) || p1.y() < p2.y();
00294 }
00295 
00296 void Containment::restore(KConfigGroup &group)
00297 {
00298     /*kDebug() << "!!!!!!!!!!!!initConstraints" << group.name() << d->type;
00299     kDebug() << "    location:" << group.readEntry("location", (int)d->location);
00300     kDebug() << "    geom:" << group.readEntry("geometry", geometry());
00301     kDebug() << "    formfactor:" << group.readEntry("formfactor", (int)d->formFactor);
00302     kDebug() << "    screen:" << group.readEntry("screen", d->screen);*/
00303     if (!isContainment()) {
00304         Applet::restore(group);
00305         return;
00306     }
00307 
00308     QRectF geo = group.readEntry("geometry", geometry());
00309     //override max/min
00310     //this ensures panels are set to their saved size even when they have max & min set to prevent
00311     //resizing
00312     if (geo.size() != geo.size().boundedTo(maximumSize())) {
00313         setMaximumSize(maximumSize().expandedTo(geo.size()));
00314     }
00315 
00316     if (geo.size() != geo.size().expandedTo(minimumSize())) {
00317         setMinimumSize(minimumSize().boundedTo(geo.size()));
00318     }
00319 
00320 
00321     resize(geo.size());
00322     //are we an offscreen containment?
00323     if (containmentType() != PanelContainment && containmentType() != CustomPanelContainment && geo.right() < 0) {
00324         corona()->addOffscreenWidget(this);
00325     }
00326 
00327     setLocation((Plasma::Location)group.readEntry("location", (int)d->location));
00328     setFormFactor((Plasma::FormFactor)group.readEntry("formfactor", (int)d->formFactor));
00329     //kDebug() << "setScreen from restore";
00330     d->lastScreen = group.readEntry("lastScreen", d->lastScreen);
00331     d->lastDesktop = group.readEntry("lastDesktop", d->lastDesktop);
00332     d->setScreen(group.readEntry("screen", d->screen), group.readEntry("desktop", d->desktop), false);
00333     QString activityId = group.readEntry("activityId", QString());
00334     if (!activityId.isEmpty()) {
00335         d->context()->setCurrentActivityId(activityId);
00336     }
00337     setActivity(group.readEntry("activity", QString()));
00338 
00339     flushPendingConstraintsEvents();
00340     restoreContents(group);
00341     setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
00342 
00343     setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
00344                  group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
00345 
00346     QMetaObject::invokeMethod(d->toolBox.data(), "restore", Q_ARG(KConfigGroup, group));
00347 
00348     KConfigGroup cfg;
00349     if (containmentType() == PanelContainment || containmentType() == CustomPanelContainment) {
00350         //don't let global desktop actions conflict with panels
00351         //this also prevents panels from sharing config with each other
00352         //but the panels aren't configurable anyways, and I doubt that'll change.
00353         d->containmentActionsSource = ContainmentPrivate::Local;
00354         cfg = KConfigGroup(&group, "ActionPlugins");
00355     } else {
00356         QString source = group.readEntry("ActionPluginsSource", QString());
00357         if (source == "Global") {
00358             cfg = KConfigGroup(corona()->config(), "ActionPlugins");
00359             d->containmentActionsSource = ContainmentPrivate::Global;
00360         } else if (source == "Activity") {
00361             cfg = KConfigGroup(corona()->config(), "Activities");
00362             cfg = KConfigGroup(&cfg, activityId);
00363             cfg = KConfigGroup(&cfg, "ActionPlugins");
00364             d->containmentActionsSource = ContainmentPrivate::Activity;
00365         } else if (source == "Local") {
00366             cfg = group;
00367             d->containmentActionsSource = ContainmentPrivate::Local;
00368         } else {
00369             //default to global
00370             //but, if there is no global config, try copying it from local.
00371             cfg = KConfigGroup(corona()->config(), "ActionPlugins");
00372             if (!cfg.exists()) {
00373                 cfg = KConfigGroup(&group, "ActionPlugins");
00374             }
00375             d->containmentActionsSource = ContainmentPrivate::Global;
00376             group.writeEntry("ActionPluginsSource", "Global");
00377         }
00378     }
00379     //kDebug() << cfg.keyList();
00380     if (cfg.exists()) {
00381         foreach (const QString &key, cfg.keyList()) {
00382             //kDebug() << "loading" << key;
00383             setContainmentActions(key, cfg.readEntry(key, QString()));
00384         }
00385     } else { //shell defaults
00386         ContainmentActionsPluginsConfig conf = corona()->containmentActionsDefaults(d->type);
00387         //steal the data directly, for efficiency
00388         QHash<QString,QString> defaults = conf.d->plugins;
00389         for (QHash<QString,QString>::const_iterator it = defaults.constBegin(),
00390                 end = defaults.constEnd(); it != end; ++it) {
00391             setContainmentActions(it.key(), it.value());
00392         }
00393     }
00394 
00395     /*
00396     kDebug() << "Containment" << id() <<
00397                 "screen" << screen() <<
00398                 "geometry is" << geometry() <<
00399                 "wallpaper" << ((d->wallpaper) ? d->wallpaper->pluginName() : QString()) <<
00400                 "wallpaper mode" << wallpaperMode() <<
00401                 "config entries" << group.entryMap();
00402     */
00403 }
00404 
00405 void Containment::save(KConfigGroup &g) const
00406 {
00407     if (Applet::d->transient) {
00408         return;
00409     }
00410 
00411     KConfigGroup group = g;
00412     if (!group.isValid()) {
00413         group = config();
00414     }
00415 
00416     // locking is saved in Applet::save
00417     Applet::save(group);
00418 
00419     if (!isContainment()) {
00420         return;
00421     }
00422 
00423     group.writeEntry("screen", d->screen);
00424     group.writeEntry("lastScreen", d->lastScreen);
00425     group.writeEntry("desktop", d->desktop);
00426     group.writeEntry("lastDesktop", d->lastDesktop);
00427     group.writeEntry("formfactor", (int)d->formFactor);
00428     group.writeEntry("location", (int)d->location);
00429     group.writeEntry("activity", d->context()->currentActivity());
00430     group.writeEntry("activityId", d->context()->currentActivityId());
00431 
00432 
00433     QMetaObject::invokeMethod(d->toolBox.data(), "save", Q_ARG(KConfigGroup, group));
00434 
00435 
00436     if (d->wallpaper) {
00437         group.writeEntry("wallpaperplugin", d->wallpaper->pluginName());
00438         group.writeEntry("wallpaperpluginmode", d->wallpaper->renderingMode().name());
00439 
00440         if (d->wallpaper->isInitialized()) {
00441             KConfigGroup wallpaperConfig(&group, "Wallpaper");
00442             wallpaperConfig = KConfigGroup(&wallpaperConfig, d->wallpaper->pluginName());
00443             d->wallpaper->save(wallpaperConfig);
00444         }
00445     }
00446 
00447     saveContents(group);
00448 }
00449 
00450 void Containment::saveContents(KConfigGroup &group) const
00451 {
00452     KConfigGroup applets(&group, "Applets");
00453     foreach (const Applet *applet, d->applets) {
00454         KConfigGroup appletConfig(&applets, QString::number(applet->id()));
00455         applet->save(appletConfig);
00456     }
00457 }
00458 
00459 void ContainmentPrivate::initApplets()
00460 {
00461     foreach (Applet *applet, applets) {
00462         applet->restore(*applet->d->mainConfigGroup());
00463         applet->init();
00464         kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Applet" << applet->name();
00465     }
00466 
00467     q->flushPendingConstraintsEvents();
00468 
00469     foreach (Applet *applet, applets) {
00470         applet->flushPendingConstraintsEvents();
00471     }
00472 
00473     kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Containment's applets initialized" << q->name();
00474 }
00475 
00476 void Containment::restoreContents(KConfigGroup &group)
00477 {
00478     KConfigGroup applets(&group, "Applets");
00479 
00480     // Sort the applet configs in order of geometry to ensure that applets
00481     // are added from left to right or top to bottom for a panel containment
00482     QList<KConfigGroup> appletConfigs;
00483     foreach (const QString &appletGroup, applets.groupList()) {
00484         //kDebug() << "reading from applet group" << appletGroup;
00485         KConfigGroup appletConfig(&applets, appletGroup);
00486         appletConfigs.append(appletConfig);
00487     }
00488     qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan);
00489 
00490     QMutableListIterator<KConfigGroup> it(appletConfigs);
00491     while (it.hasNext()) {
00492         KConfigGroup &appletConfig = it.next();
00493         int appId = appletConfig.name().toUInt();
00494         QString plugin = appletConfig.readEntry("plugin", QString());
00495 
00496         if (plugin.isEmpty()) {
00497             continue;
00498         }
00499 
00500         d->addApplet(plugin, QVariantList(), appletConfig.readEntry("geometry", QRectF()), appId, true);
00501     }
00502 }
00503 
00504 Containment::Type Containment::containmentType() const
00505 {
00506     return d->type;
00507 }
00508 
00509 void Containment::setContainmentType(Containment::Type type)
00510 {
00511     if (d->type == type) {
00512         return;
00513     }
00514 
00515     delete d->toolBox.data();
00516     d->type = type;
00517     d->checkContainmentFurniture();
00518 }
00519 
00520 void ContainmentPrivate::checkContainmentFurniture()
00521 {
00522     if (q->isContainment() &&
00523         (type == Containment::DesktopContainment || type == Containment::PanelContainment)) {
00524         createToolBox();
00525     }
00526 }
00527 
00528 Corona *Containment::corona() const
00529 {
00530     return qobject_cast<Corona*>(scene());
00531 }
00532 
00533 void Containment::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00534 {
00535     event->ignore();
00536     if (d->wallpaper && d->wallpaper->isInitialized()) {
00537         QGraphicsItem *item = scene()->itemAt(event->scenePos());
00538         if (item == this) {
00539             d->wallpaper->mouseMoveEvent(event);
00540         }
00541     }
00542 
00543     if (!event->isAccepted()) {
00544         event->accept();
00545         Applet::mouseMoveEvent(event);
00546     }
00547 }
00548 
00549 void Containment::mousePressEvent(QGraphicsSceneMouseEvent *event)
00550 {
00551     event->ignore();
00552     if (d->appletAt(event->scenePos())) {
00553         return; //no unexpected click-throughs
00554     }
00555 
00556     if (d->wallpaper && d->wallpaper->isInitialized() && !event->isAccepted()) {
00557         d->wallpaper->mousePressEvent(event);
00558     }
00559 
00560     if (event->isAccepted()) {
00561         setFocus(Qt::MouseFocusReason);
00562     } else if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) {
00563         // we'll catch this in the context menu even
00564         Applet::mousePressEvent(event);
00565     } else {
00566         QString trigger = ContainmentActions::eventToString(event);
00567         if (d->prepareContainmentActions(trigger, event->screenPos())) {
00568             d->actionPlugins()->value(trigger)->contextEvent(event);
00569         }
00570 
00571         if (!event->isAccepted()) {
00572             Applet::mousePressEvent(event);
00573         }
00574     }
00575 }
00576 
00577 void Containment::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00578 {
00579     event->ignore();
00580     if (d->appletAt(event->scenePos())) {
00581         return; //no unexpected click-throughs
00582     }
00583 
00584     QString trigger = ContainmentActions::eventToString(event);
00585 
00586     if (d->wallpaper && d->wallpaper->isInitialized()) {
00587         d->wallpaper->mouseReleaseEvent(event);
00588     }
00589 
00590     if (!event->isAccepted() && isContainment()) {
00591         if (d->prepareContainmentActions(trigger, event->screenPos())) {
00592             d->actionPlugins()->value(trigger)->contextEvent(event);
00593         }
00594 
00595         event->accept();
00596         Applet::mouseReleaseEvent(event);
00597     }
00598 }
00599 
00600 void Containment::showDropZone(const QPoint pos)
00601 {
00602     Q_UNUSED(pos)
00603     //Base implementation does nothing, don't put code here
00604 }
00605 
00606 void Containment::showContextMenu(const QPointF &containmentPos, const QPoint &screenPos)
00607 {
00608     //kDebug() << containmentPos << screenPos;
00609     QGraphicsSceneContextMenuEvent gvevent;
00610     gvevent.setScreenPos(screenPos);
00611     gvevent.setScenePos(mapToScene(containmentPos));
00612     gvevent.setPos(containmentPos);
00613     gvevent.setReason(QGraphicsSceneContextMenuEvent::Mouse);
00614     gvevent.setWidget(view());
00615     contextMenuEvent(&gvevent);
00616 }
00617 
00618 void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
00619 {
00620     if (!isContainment() || !KAuthorized::authorizeKAction("plasma/containment_context_menu")) {
00621         Applet::contextMenuEvent(event);
00622         return;
00623     }
00624 
00625     KMenu desktopMenu;
00626     Applet *applet = d->appletAt(event->scenePos());
00627     //kDebug() << "context menu event " << (QObject*)applet;
00628 
00629     if (applet) {
00630         d->addAppletActions(desktopMenu, applet, event);
00631     } else {
00632         d->addContainmentActions(desktopMenu, event);
00633     }
00634 
00635     //kDebug() << "executing at" << screenPos;
00636     QMenu *menu = &desktopMenu;
00637     //kDebug() << "showing menu, actions" << desktopMenu.actions().size() << desktopMenu.actions().first()->menu();
00638     if (desktopMenu.actions().size() == 1 && desktopMenu.actions().first()->menu()) {
00639         // we have a menu with a single top level menu; just show that top level menu instad.
00640         menu = desktopMenu.actions().first()->menu();
00641     }
00642 
00643     if (!menu->isEmpty()) {
00644         QPoint pos = event->screenPos();
00645         if (applet && d->isPanelContainment()) {
00646             menu->adjustSize();
00647             pos = applet->popupPosition(menu->size());
00648             if (event->reason() == QGraphicsSceneContextMenuEvent::Mouse) {
00649                 // if the menu pops up way away from the mouse press, then move it
00650                 // to the mouse press
00651                 if (d->formFactor == Vertical) {
00652                     if (pos.y() + menu->height() < event->screenPos().y()) {
00653                         pos.setY(event->screenPos().y());
00654                     }
00655                 } else if (d->formFactor == Horizontal) {
00656                     if (pos.x() + menu->width() < event->screenPos().x()) {
00657                         pos.setX(event->screenPos().x());
00658                     }
00659                 }
00660             }
00661         }
00662 
00663         menu->exec(pos);
00664         event->accept();
00665     } else {
00666         Applet::contextMenuEvent(event);
00667     }
00668 }
00669 
00670 void ContainmentPrivate::addContainmentActions(KMenu &desktopMenu, QEvent *event)
00671 {
00672     if (static_cast<Corona*>(q->scene())->immutability() != Mutable &&
00673         !KAuthorized::authorizeKAction("plasma/containment_actions")) {
00674         //kDebug() << "immutability";
00675         return;
00676     }
00677 
00678     const QString trigger = ContainmentActions::eventToString(event);
00679     prepareContainmentActions(trigger, QPoint(), &desktopMenu);
00680 }
00681 
00682 void ContainmentPrivate::addAppletActions(KMenu &desktopMenu, Applet *applet, QEvent *event)
00683 {
00684     foreach (QAction *action, applet->contextualActions()) {
00685         if (action) {
00686             desktopMenu.addAction(action);
00687         }
00688     }
00689 
00690     if (!applet->d->failed) {
00691         QAction *configureApplet = applet->d->actions->action("configure");
00692         if (configureApplet && configureApplet->isEnabled()) {
00693             desktopMenu.addAction(configureApplet);
00694         }
00695 
00696         QAction *runAssociatedApplication = applet->d->actions->action("run associated application");
00697         if (runAssociatedApplication && runAssociatedApplication->isEnabled()) {
00698             desktopMenu.addAction(runAssociatedApplication);
00699         }
00700     }
00701 
00702     KMenu *containmentMenu = new KMenu(i18nc("%1 is the name of the containment", "%1 Options", q->name()), &desktopMenu);
00703     addContainmentActions(*containmentMenu, event);
00704     if (!containmentMenu->isEmpty()) {
00705         int enabled = 0;
00706         //count number of real actions
00707         QListIterator<QAction *> actionsIt(containmentMenu->actions());
00708         while (enabled < 3 && actionsIt.hasNext()) {
00709             QAction *action = actionsIt.next();
00710             if (action->isVisible() && !action->isSeparator()) {
00711                 ++enabled;
00712             }
00713         }
00714 
00715         if (enabled) {
00716             //if there is only one, don't create a submenu
00717             if (enabled < 2) {
00718                 foreach (QAction *action, containmentMenu->actions()) {
00719                     if (action->isVisible() && !action->isSeparator()) {
00720                         desktopMenu.addAction(action);
00721                     }
00722                 }
00723             } else {
00724                 desktopMenu.addMenu(containmentMenu);
00725             }
00726         }
00727     }
00728 
00729     if (q->immutability() == Mutable) {
00730         QAction *closeApplet = applet->d->actions->action("remove");
00731         //kDebug() << "checking for removal" << closeApplet;
00732         if (closeApplet) {
00733             if (!desktopMenu.isEmpty()) {
00734                 desktopMenu.addSeparator();
00735             }
00736 
00737             //kDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible();
00738             desktopMenu.addAction(closeApplet);
00739         }
00740     }
00741 }
00742 
00743 Applet* ContainmentPrivate::appletAt(const QPointF &point)
00744 {
00745     Applet *applet = 0;
00746 
00747     QGraphicsItem *item = q->scene()->itemAt(point);
00748     if (item == q) {
00749         item = 0;
00750     }
00751 
00752     while (item) {
00753         if (item->isWidget()) {
00754             applet = qobject_cast<Applet*>(static_cast<QGraphicsWidget*>(item));
00755             if (applet) {
00756                 if (applet->isContainment()) {
00757                     applet = 0;
00758                 }
00759                 break;
00760             }
00761         }
00762         AppletHandle *handle = dynamic_cast<AppletHandle*>(item);
00763         if (handle) {
00764             //pretend it was on the applet
00765             applet = handle->applet();
00766             break;
00767         }
00768         item = item->parentItem();
00769     }
00770     return applet;
00771 }
00772 
00773 void Containment::setFormFactor(FormFactor formFactor)
00774 {
00775     if (d->formFactor == formFactor) {
00776         return;
00777     }
00778 
00779     //kDebug() << "switching FF to " << formFactor;
00780     d->formFactor = formFactor;
00781 
00782     if (isContainment() &&
00783         (d->type == PanelContainment || d->type == CustomPanelContainment)) {
00784         // we are a panel and we have chaged our orientation
00785         d->positionPanel(true);
00786     }
00787 
00788     QMetaObject::invokeMethod(d->toolBox.data(), "reposition");
00789 
00790     updateConstraints(Plasma::FormFactorConstraint);
00791 
00792     KConfigGroup c = config();
00793     c.writeEntry("formfactor", (int)formFactor);
00794     emit configNeedsSaving();
00795 }
00796 
00797 void Containment::setLocation(Location location)
00798 {
00799     if (d->location == location) {
00800         return;
00801     }
00802 
00803     bool emitGeomChange = false;
00804 
00805     if ((location == TopEdge || location == BottomEdge) &&
00806         (d->location == TopEdge || d->location == BottomEdge)) {
00807         emitGeomChange = true;
00808     }
00809 
00810     if ((location == RightEdge || location == LeftEdge) &&
00811         (d->location == RightEdge || d->location == LeftEdge)) {
00812         emitGeomChange = true;
00813     }
00814 
00815     d->location = location;
00816 
00817     foreach (Applet *applet, d->applets) {
00818         applet->updateConstraints(Plasma::LocationConstraint);
00819     }
00820 
00821     if (emitGeomChange) {
00822         // our geometry on the scene will not actually change,
00823         // but for the purposes of views it has
00824         emit geometryChanged();
00825     }
00826 
00827     updateConstraints(Plasma::LocationConstraint);
00828 
00829     KConfigGroup c = config();
00830     c.writeEntry("location", (int)location);
00831     emit configNeedsSaving();
00832 }
00833 
00834 void Containment::addSiblingContainment()
00835 {
00836     emit addSiblingContainment(this);
00837 }
00838 
00839 void Containment::clearApplets()
00840 {
00841     foreach (Applet *applet, d->applets) {
00842         applet->d->cleanUpAndDelete();
00843     }
00844 
00845     d->applets.clear();
00846 }
00847 
00848 Applet *Containment::addApplet(const QString &name, const QVariantList &args,
00849                                const QRectF &appletGeometry)
00850 {
00851     return d->addApplet(name, args, appletGeometry);
00852 }
00853 
00854 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit)
00855 {
00856     if (!isContainment() || (!delayInit && immutability() != Mutable)) {
00857         return;
00858     }
00859 
00860     if (!applet) {
00861         kDebug() << "adding null applet!?!";
00862         return;
00863     }
00864 
00865     if (d->applets.contains(applet)) {
00866         kDebug() << "already have this applet!";
00867     }
00868 
00869     Containment *currentContainment = applet->containment();
00870 
00871     if (d->type == PanelContainment) {
00872         //panels don't want backgrounds, which is important when setting geometry
00873         setBackgroundHints(NoBackground);
00874     }
00875 
00876     if (currentContainment && currentContainment != this) {
00877         emit currentContainment->appletRemoved(applet);
00878         if (currentContainment->d->focusedApplet == applet) {
00879             currentContainment->d->focusedApplet = 0;
00880         }
00881 
00882         disconnect(applet, 0, currentContainment, 0);
00883         KConfigGroup oldConfig = applet->config();
00884         currentContainment->d->applets.removeAll(applet);
00885         applet->setParentItem(this);
00886         applet->setParent(this);
00887 
00888         // now move the old config to the new location
00889         //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc
00890         KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
00891         oldConfig.reparent(&c);
00892         applet->d->resetConfigurationObject();
00893 
00894         disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate()));
00895     } else {
00896         applet->setParentItem(this);
00897         applet->setParent(this);
00898     }
00899 
00900     d->applets << applet;
00901 
00902     connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
00903     connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus()));
00904     connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*)));
00905     connect(applet, SIGNAL(newStatus(Plasma::ItemStatus)), this, SLOT(checkStatus(Plasma::ItemStatus)));
00906     connect(applet, SIGNAL(activate()), this, SIGNAL(activate()));
00907 
00908     if (pos != QPointF(-1, -1)) {
00909         applet->setPos(pos);
00910     }
00911 
00912     if (!delayInit && !currentContainment) {
00913         applet->restore(*applet->d->mainConfigGroup());
00914         applet->init();
00915         Plasma::Animation *anim = Plasma::Animator::create(Plasma::Animator::AppearAnimation);
00916         if (anim) {
00917             connect(anim, SIGNAL(finished()), this, SLOT(appletAppearAnimationComplete()));
00918             anim->setTargetWidget(applet);
00919             //FIXME: small hack until we have proper js anim support; allows 'zoom' to work in the
00920             //'right' direction for appearance
00921             anim->setDirection(QAbstractAnimation::Backward);
00922             anim->start(QAbstractAnimation::DeleteWhenStopped);
00923         } else {
00924             d->appletAppeared(applet);
00925         }
00926     }
00927 
00928     applet->setFlag(QGraphicsItem::ItemIsMovable, true);
00929     applet->updateConstraints(Plasma::AllConstraints);
00930     if (!delayInit) {
00931         applet->flushPendingConstraintsEvents();
00932     }
00933     emit appletAdded(applet, pos);
00934 
00935     if (!currentContainment) {
00936         applet->updateConstraints(Plasma::StartupCompletedConstraint);
00937         if (!delayInit) {
00938             applet->flushPendingConstraintsEvents();
00939         }
00940     }
00941 
00942     if (!delayInit) {
00943         applet->d->scheduleModificationNotification();
00944     }
00945 }
00946 
00947 Applet::List Containment::applets() const
00948 {
00949     return d->applets;
00950 }
00951 
00952 void Containment::setScreen(int newScreen, int newDesktop)
00953 {
00954     d->setScreen(newScreen, newDesktop);
00955 }
00956 
00957 void ContainmentPrivate::setScreen(int newScreen, int newDesktop, bool preventInvalidDesktops)
00958 {
00959     // What we want to do in here is:
00960     //   * claim the screen as our own
00961     //   * signal whatever may be watching this containment about the switch
00962     //   * if we are a full screen containment, then:
00963     //      * resize to match the screen if we're that kind of containment
00964     //      * kick other full-screen containments off this screen
00965     //          * if we had a screen, then give our screen to the containment
00966     //            we kick out
00967     //
00968     // a screen of -1 means no associated screen.
00969     Corona *corona = q->corona();
00970     Q_ASSERT(corona);
00971 
00972     //if it's an offscreen widget, don't allow to claim a screen, after all it's *off*screen
00973     if (corona->offscreenWidgets().contains(q)) {
00974         return;
00975     }
00976 
00977     int numScreens = corona->numScreens();
00978     if (newScreen < -1) {
00979         newScreen = -1;
00980     }
00981 
00982     // -1 == All desktops
00983     if (newDesktop < -1 || (preventInvalidDesktops && newDesktop > KWindowSystem::numberOfDesktops() - 1)) {
00984         newDesktop = -1;
00985     }
00986 
00987     //kDebug() << activity() << "setting screen to " << newScreen << newDesktop << "and type is" << type;
00988 
00989     Containment *swapScreensWith(0);
00990     const bool isDesktopContainment = type == Containment::DesktopContainment ||
00991                                       type == Containment::CustomContainment;
00992     if (isDesktopContainment) {
00993         // we want to listen to changes in work area if our screen changes
00994         if (toolBox) {
00995             if (screen < 0 && newScreen > -1) {
00996                 QObject::connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()), Qt::UniqueConnection);
00997             } else if (newScreen < 0) {
00998                 QObject::disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()));
00999             }
01000         }
01001 
01002         if (newScreen > -1) {
01003             // sanity check to make sure someone else doesn't have this screen already!
01004             Containment *currently = corona->containmentForScreen(newScreen, newDesktop);
01005             if (currently && currently != q) {
01006                 kDebug() << "currently is on screen" << currently->screen()
01007                          << "desktop" << currently->desktop()
01008                          << "and is" << currently->activity()
01009                          << (QObject*)currently << "i'm" << (QObject*)q;
01010                 currently->setScreen(-1, currently->desktop());
01011                 swapScreensWith = currently;
01012             }
01013         }
01014     }
01015 
01016     if (newScreen < numScreens && newScreen > -1 && isDesktopContainment) {
01017         q->resize(corona->screenGeometry(newScreen).size());
01018     }
01019 
01020     int oldDesktop = desktop;
01021     desktop = newDesktop;
01022 
01023     int oldScreen = screen;
01024     screen = newScreen;
01025 
01026     q->updateConstraints(Plasma::ScreenConstraint);
01027 
01028     if (oldScreen != newScreen || oldDesktop != newDesktop) {
01029         /*
01030         kDebug() << "going to signal change for" << q
01031                  << ", old screen & desktop:" << oldScreen << oldDesktop
01032                  << ", new:" << screen << desktop;
01033                  */
01034         KConfigGroup c = q->config();
01035         c.writeEntry("screen", screen);
01036         c.writeEntry("desktop", desktop);
01037         if (newScreen != -1) {
01038             lastScreen = newScreen;
01039             lastDesktop = newDesktop;
01040             c.writeEntry("lastScreen", lastScreen);
01041             c.writeEntry("lastDesktop", lastDesktop);
01042         }
01043         emit q->configNeedsSaving();
01044         emit q->screenChanged(oldScreen, newScreen, q);
01045     }
01046 
01047     if (swapScreensWith) {
01048         //kDebug() << "setScreen due to swap, part 2";
01049         swapScreensWith->setScreen(oldScreen, oldDesktop);
01050     }
01051 
01052     checkRemoveAction();
01053 
01054     if (newScreen >= 0) {
01055         emit q->activate();
01056     }
01057 }
01058 
01059 int Containment::screen() const
01060 {
01061     return d->screen;
01062 }
01063 
01064 int Containment::lastScreen() const
01065 {
01066     return d->lastScreen;
01067 }
01068 
01069 int Containment::desktop() const
01070 {
01071     return d->desktop;
01072 }
01073 
01074 int Containment::lastDesktop() const
01075 {
01076     return d->lastDesktop;
01077 }
01078 
01079 KPluginInfo::List Containment::listContainments(const QString &category,
01080                                                 const QString &parentApp)
01081 {
01082     return listContainmentsOfType(QString(), category, parentApp);
01083 }
01084 
01085 
01086 KPluginInfo::List Containment::listContainmentsOfType(const QString &type,
01087                                                       const QString &category,
01088                                                       const QString &parentApp)
01089 {
01090     QString constraint;
01091 
01092     if (parentApp.isEmpty()) {
01093         constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')");
01094     } else {
01095         constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
01096     }
01097 
01098     if (!type.isEmpty()) {
01099         if (!constraint.isEmpty()) {
01100             constraint.append(" and ");
01101         }
01102 
01103         constraint.append("'").append(type).append("' ~in [X-Plasma-ContainmentCategories]");
01104     }
01105 
01106     if (!category.isEmpty()) {
01107         if (!constraint.isEmpty()) {
01108             constraint.append(" and ");
01109         }
01110 
01111         constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
01112         if (category == "Miscellaneous") {
01113             constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
01114         }
01115     }
01116 
01117     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
01118     //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches";
01119     return KPluginInfo::fromServices(offers);
01120 }
01121 
01122 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype)
01123 {
01124     const QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
01125     //kDebug() << mimetype << constraint;
01126     const KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
01127     return KPluginInfo::fromServices(offers);
01128 }
01129 
01130 QStringList Containment::listContainmentTypes()
01131 {
01132     KPluginInfo::List containmentInfos = listContainments();
01133     QSet<QString> types;
01134 
01135     foreach (const KPluginInfo &containmentInfo, containmentInfos) {
01136         QStringList theseTypes = containmentInfo.service()->property("X-Plasma-ContainmentCategories").toStringList();
01137         foreach (const QString &type, theseTypes) {
01138             types.insert(type);
01139         }
01140     }
01141 
01142     return types.toList();
01143 }
01144 
01145 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
01146 {
01147     //kDebug() << immutability() << Mutable << (immutability() == Mutable);
01148     event->setAccepted(immutability() == Mutable &&
01149                        (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) ||
01150                         KUrl::List::canDecode(event->mimeData()) ||
01151                         event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())));
01152 
01153     if (!event->isAccepted()) {
01154         // check to see if we have an applet that accepts the format.
01155         QStringList formats = event->mimeData()->formats();
01156 
01157         foreach (const QString &format, formats) {
01158             KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format);
01159             if (!appletList.isEmpty()) {
01160                 event->setAccepted(true);
01161                 break;
01162             }
01163         }
01164 
01165         if (!event->isAccepted()) {
01166             foreach (const QString &format, formats) {
01167                 KPluginInfo::List wallpaperList = Wallpaper::listWallpaperInfoForMimetype(format);
01168                 if (!wallpaperList.isEmpty()) {
01169                     event->setAccepted(true);
01170                     break;
01171                 }
01172             }
01173         }
01174     }
01175 
01176     if (event->isAccepted()) {
01177         if (d->dropZoneStarted) {
01178             showDropZone(event->pos().toPoint());
01179         } else {
01180             if (!d->showDropZoneDelayTimer) {
01181                 d->showDropZoneDelayTimer = new QTimer(this);
01182                 d->showDropZoneDelayTimer->setInterval(300);
01183                 d->showDropZoneDelayTimer->setSingleShot(true);
01184                 connect(d->showDropZoneDelayTimer, SIGNAL(timeout()), this, SLOT(showDropZoneDelayed()));
01185             }
01186 
01187             d->dropPoints.insert(0, event->pos());
01188             d->showDropZoneDelayTimer->start();
01189         }
01190     }
01191 }
01192 
01193 void Containment::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
01194 {
01195     //kDebug() << event->pos() << size().height() << size().width();
01196     if (d->showDropZoneDelayTimer) {
01197         d->showDropZoneDelayTimer->stop();
01198     }
01199 
01200     if (event->pos().y() < 1 || event->pos().y() > size().height() ||
01201         event->pos().x() < 1 || event->pos().x() > size().width()) {
01202         showDropZone(QPoint());
01203         d->dropZoneStarted = false;
01204     }
01205 }
01206 
01207 void ContainmentPrivate::showDropZoneDelayed()
01208 {
01209     dropZoneStarted = true;
01210     q->showDropZone(dropPoints.value(0).toPoint());
01211     dropPoints.remove(0);
01212 }
01213 
01214 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
01215 {
01216     QGraphicsItem *item = scene()->itemAt(event->scenePos());
01217     event->setAccepted(item == this || item == d->toolBox.data() || !item);
01218     //kDebug() << event->isAccepted() << d->showDropZoneDelayTimer->isActive();
01219     if (!event->isAccepted()) {
01220         if (d->showDropZoneDelayTimer) {
01221             d->showDropZoneDelayTimer->stop();
01222         }
01223     } else if (!d->showDropZoneDelayTimer->isActive() && immutability() == Plasma::Mutable) {
01224         showDropZone(event->pos().toPoint());
01225     }
01226 }
01227 
01228 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event)
01229 {
01230     if (isContainment()) {
01231         d->dropData(event->scenePos(), event->screenPos(), event);
01232     } else {
01233         Applet::dropEvent(event);
01234     }
01235 }
01236 
01237 void ContainmentPrivate::dropData(QPointF scenePos, QPoint screenPos, QGraphicsSceneDragDropEvent *dropEvent)
01238 {
01239     if (q->immutability() != Mutable) {
01240         return;
01241     }
01242 
01243     QPointF pos = q->mapFromScene(scenePos);
01244     const QMimeData *mimeData = 0;
01245 
01246     if (dropEvent) {
01247         mimeData = dropEvent->mimeData();
01248     } else {
01249         QClipboard *clipboard = QApplication::clipboard();
01250         mimeData = clipboard->mimeData(QClipboard::Selection);
01251         //TODO if that's not supported (ie non-linux) should we try clipboard instead of selection?
01252     }
01253 
01254     if (!mimeData) {
01255         //Selection is either empty or not supported on this OS
01256         kDebug() << "no mime data";
01257         return;
01258     }
01259 
01260     //kDebug() << event->mimeData()->text();
01261 
01262     QString appletMimetype(q->corona() ? q->corona()->appletMimeType() : QString());
01263 
01264     if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
01265         QString data = mimeData->data(appletMimetype);
01266         const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
01267         foreach (const QString &appletName, appletNames) {
01268             //kDebug() << "doing" << appletName;
01269             QRectF geom(pos, QSize(0, 0));
01270             q->addApplet(appletName, QVariantList(), geom);
01271         }
01272         if (dropEvent) {
01273             dropEvent->acceptProposedAction();
01274         }
01275     } else if (mimeData->hasFormat(ExtenderItemMimeData::mimeType())) {
01276         kDebug() << "mimetype plasma/extenderitem is dropped, creating internal:extender";
01277         //Handle dropping extenderitems.
01278         const ExtenderItemMimeData *extenderData = qobject_cast<const ExtenderItemMimeData*>(mimeData);
01279         if (extenderData) {
01280             ExtenderItem *item = extenderData->extenderItem();
01281             QRectF geometry(pos - extenderData->pointerOffset(), item->size());
01282             kDebug() << "desired geometry: " << geometry;
01283             Applet *applet = qobject_cast<ExtenderApplet *>(item->extender() ?  item->extender()->applet() : 0);
01284             if (applet) {
01285                 qreal left, top, right, bottom;
01286                 applet->getContentsMargins(&left, &top, &right, &bottom);
01287                 applet->setPos(geometry.topLeft() - QPointF(int(left), int(top)));
01288                 applet->show();
01289             } else {
01290                 applet = addApplet("internal:extender", QVariantList(), geometry, 0, true);
01291                 applet->hide();
01292                 applet->init();
01293                 appletAppeared(applet);
01294                 applet->flushPendingConstraintsEvents();
01295                 applet->d->scheduleModificationNotification();
01296                 applet->adjustSize();
01297                 applet->show();
01298             }
01299             item->setExtender(applet->extender());
01300         }
01301     } else if (KUrl::List::canDecode(mimeData)) {
01302         //TODO: collect the mimetypes of available script engines and offer
01303         //      to create widgets out of the matching URLs, if any
01304         const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
01305         foreach (const KUrl &url, urls) {
01306             if (AccessManager::supportedProtocols().contains(url.protocol())) {
01307                 AccessAppletJob *job = AccessManager::self()->accessRemoteApplet(url);
01308                 if (dropEvent) {
01309                     dropPoints[job] = dropEvent->pos();
01310                 } else {
01311                     dropPoints[job] = scenePos;
01312                 }
01313                 QObject::connect(AccessManager::self(), SIGNAL(finished(Plasma::AccessAppletJob*)),
01314                                  q, SLOT(remoteAppletReady(Plasma::AccessAppletJob*)));
01315             }
01316 #ifndef PLASMA_NO_KIO
01317             else {
01318                 KMimeType::Ptr mime = KMimeType::findByUrl(url);
01319                 QString mimeName = mime->name();
01320                 QRectF geom(pos, QSize());
01321                 QVariantList args;
01322                 args << url.url();
01323                 kDebug() << "can decode" << mimeName << args;
01324 
01325                 // It may be a directory or a file, let's stat
01326                 KIO::JobFlags flags = KIO::HideProgressInfo;
01327                 KIO::MimetypeJob *job = KIO::mimetype(url, flags);
01328                 if (dropEvent) {
01329                     dropPoints[job] = dropEvent->pos();
01330                 } else {
01331                     dropPoints[job] = scenePos;
01332                 }
01333 
01334                 QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(dropJobResult(KJob*)));
01335                 QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)),
01336                                  q, SLOT(mimeTypeRetrieved(KIO::Job*,QString)));
01337 
01338                 KMenu *choices = new KMenu("Content dropped");
01339                 choices->addAction(KIcon("process-working"), i18n("Fetching file type..."));
01340                 if (dropEvent) {
01341                     choices->popup(dropEvent->screenPos());
01342                 } else {
01343                     choices->popup(screenPos);
01344                 }
01345 
01346                 dropMenus[job] = choices;
01347             }
01348 #endif
01349         }
01350 
01351         if (dropEvent) {
01352             dropEvent->acceptProposedAction();
01353         }
01354     } else {
01355         QStringList formats = mimeData->formats();
01356         QHash<QString, KPluginInfo> seenPlugins;
01357         QHash<QString, QString> pluginFormats;
01358 
01359         foreach (const QString &format, formats) {
01360             KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format);
01361 
01362             foreach (const KPluginInfo &plugin, plugins) {
01363                 if (seenPlugins.contains(plugin.pluginName())) {
01364                     continue;
01365                 }
01366 
01367                 seenPlugins.insert(plugin.pluginName(), plugin);
01368                 pluginFormats.insert(plugin.pluginName(), format);
01369             }
01370         }
01371         //kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values();
01372 
01373         QString selectedPlugin;
01374 
01375         if (seenPlugins.isEmpty()) {
01376             // do nothing
01377         } else if (seenPlugins.count() == 1) {
01378             selectedPlugin = seenPlugins.constBegin().key();
01379         } else {
01380             KMenu choices;
01381             QHash<QAction *, QString> actionsToPlugins;
01382             foreach (const KPluginInfo &info, seenPlugins) {
01383                 QAction *action;
01384                 if (!info.icon().isEmpty()) {
01385                     action = choices.addAction(KIcon(info.icon()), info.name());
01386                 } else {
01387                     action = choices.addAction(info.name());
01388                 }
01389 
01390                 actionsToPlugins.insert(action, info.pluginName());
01391             }
01392 
01393             QAction *choice = choices.exec(screenPos);
01394             if (choice) {
01395                 selectedPlugin = actionsToPlugins[choice];
01396             }
01397         }
01398 
01399         if (!selectedPlugin.isEmpty()) {
01400             if (!dropEvent) {
01401                 // since we may have entered an event loop up above with the menu,
01402                 // the clipboard item may no longer be valid, as QClipboard resets
01403                 // the object behind the back of the application with a zero timer
01404                 // so we fetch it again here
01405                 QClipboard *clipboard = QApplication::clipboard();
01406                 mimeData = clipboard->mimeData(QClipboard::Selection);
01407             }
01408 
01409             KTemporaryFile tempFile;
01410             if (mimeData && tempFile.open()) {
01411                 //TODO: what should we do with files after the applet is done with them??
01412                 tempFile.setAutoRemove(false);
01413 
01414                 {
01415                     QDataStream stream(&tempFile);
01416                     QByteArray data = mimeData->data(pluginFormats[selectedPlugin]);
01417                     stream.writeRawData(data, data.size());
01418                 }
01419 
01420                 QRectF geom(pos, QSize());
01421                 QVariantList args;
01422                 args << tempFile.fileName();
01423                 kDebug() << args;
01424                 tempFile.close();
01425 
01426                 q->addApplet(selectedPlugin, args, geom);
01427             }
01428         }
01429     }
01430 }
01431 
01432 void ContainmentPrivate::clearDataForMimeJob(KIO::Job *job)
01433 {
01434 #ifndef PLASMA_NO_KIO
01435     QObject::disconnect(job, 0, q, 0);
01436     dropPoints.remove(job);
01437     KMenu *choices = dropMenus.take(job);
01438     delete choices;
01439     job->kill();
01440 #endif // PLASMA_NO_KIO
01441 }
01442 
01443 void ContainmentPrivate::remoteAppletReady(Plasma::AccessAppletJob *job)
01444 {
01445     QPointF pos = dropPoints.take(job);
01446     if (job->error()) {
01447         //TODO: nice user visible error handling (knotification probably?)
01448         kDebug() << "remote applet access failed: " << job->errorText();
01449         return;
01450     }
01451 
01452     if (!job->applet()) {
01453         kDebug() << "how did we end up here? if applet is null, the job->error should be nonzero";
01454         return;
01455     }
01456 
01457     q->addApplet(job->applet(), pos);
01458 }
01459 
01460 void ContainmentPrivate::dropJobResult(KJob *job)
01461 {
01462 #ifndef PLASMA_NO_KIO
01463     KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
01464     if (!tjob) {
01465         kDebug() << "job is not a KIO::TransferJob, won't handle the drop...";
01466         clearDataForMimeJob(tjob);
01467         return;
01468     }
01469     if (job->error()) {
01470         kDebug() << "ERROR" << tjob->error() << ' ' << tjob->errorString();
01471     }
01472     // We call mimetypeRetrieved since there might be other mechanisms
01473     // for finding suitable applets. Cleanup happens there as well.
01474     mimeTypeRetrieved(qobject_cast<KIO::Job *>(job), QString());
01475 #endif // PLASMA_NO_KIO
01476 }
01477 
01478 void ContainmentPrivate::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype)
01479 {
01480 #ifndef PLASMA_NO_KIO
01481     kDebug() << "Mimetype Job returns." << mimetype;
01482     KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
01483     if (!tjob) {
01484         kDebug() << "job should be a TransferJob, but isn't";
01485         clearDataForMimeJob(job);
01486         return;
01487     }
01488     KPluginInfo::List appletList = Applet::listAppletInfoForUrl(tjob->url());
01489     if (mimetype.isEmpty() && !appletList.count()) {
01490         clearDataForMimeJob(job);
01491         kDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")";
01492         return;
01493     } else {
01494 
01495         QPointF posi; // will be overwritten with the event's position
01496         if (dropPoints.keys().contains(tjob)) {
01497             posi = dropPoints[tjob];
01498             kDebug() << "Received a suitable dropEvent at" << posi;
01499         } else {
01500             kDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob";
01501             clearDataForMimeJob(job);
01502             return;
01503         }
01504 
01505         KMenu *choices = dropMenus.value(tjob);
01506         if (!choices) {
01507             kDebug() << "Bailing out. No QMenu found for this job.";
01508             clearDataForMimeJob(job);
01509             return;
01510         }
01511 
01512         QVariantList args;
01513         args << tjob->url().url() << mimetype;
01514 
01515         kDebug() << "Creating menu for:" << mimetype  << posi << args;
01516 
01517         appletList << Applet::listAppletInfoForMimetype(mimetype);
01518         KPluginInfo::List wallpaperList;
01519         if (drawWallpaper) {
01520             if (wallpaper && wallpaper->supportsMimetype(mimetype)) {
01521                 wallpaperList << wallpaper->d->wallpaperDescription;
01522             } else {
01523                 wallpaperList = Wallpaper::listWallpaperInfoForMimetype(mimetype);
01524             }
01525         }
01526 
01527         if (!appletList.isEmpty() || !wallpaperList.isEmpty()) {
01528             choices->clear();
01529             QHash<QAction *, QString> actionsToApplets;
01530             choices->addTitle(i18n("Widgets"));
01531             foreach (const KPluginInfo &info, appletList) {
01532                 kDebug() << info.name();
01533                 QAction *action;
01534                 if (!info.icon().isEmpty()) {
01535                     action = choices->addAction(KIcon(info.icon()), info.name());
01536                 } else {
01537                     action = choices->addAction(info.name());
01538                 }
01539 
01540                 actionsToApplets.insert(action, info.pluginName());
01541                 kDebug() << info.pluginName();
01542             }
01543             actionsToApplets.insert(choices->addAction(i18n("Icon")), "icon");
01544 
01545             QHash<QAction *, QString> actionsToWallpapers;
01546             if (!wallpaperList.isEmpty())  {
01547                 choices->addTitle(i18n("Wallpaper"));
01548 
01549                 QMap<QString, KPluginInfo> sorted;
01550                 foreach (const KPluginInfo &info, appletList) {
01551                     sorted.insert(info.name(), info);
01552                 }
01553 
01554                 foreach (const KPluginInfo &info, wallpaperList) {
01555                     QAction *action;
01556                     if (!info.icon().isEmpty()) {
01557                         action = choices->addAction(KIcon(info.icon()), info.name());
01558                     } else {
01559                         action = choices->addAction(info.name());
01560                     }
01561 
01562                     actionsToWallpapers.insert(action, info.pluginName());
01563                 }
01564             }
01565 
01566             QAction *choice = choices->exec();
01567             if (choice) {
01568                 // Put the job on hold so it can be recycled to fetch the actual content,
01569                 // which is to be expected when something's dropped onto the desktop and
01570                 // an applet is to be created with this URL
01571                 if (!mimetype.isEmpty() && !tjob->error()) {
01572                     tjob->putOnHold();
01573                     KIO::Scheduler::publishSlaveOnHold();
01574                 }
01575                 QString plugin = actionsToApplets.value(choice);
01576                 if (plugin.isEmpty()) {
01577                     //set wallpapery stuff
01578                     plugin = actionsToWallpapers.value(choice);
01579                     if (!wallpaper || plugin != wallpaper->pluginName()) {
01580                         kDebug() << "Wallpaper dropped:" << tjob->url();
01581                         q->setWallpaper(plugin);
01582                     }
01583 
01584                     if (wallpaper) {
01585                         kDebug() << "Wallpaper dropped:" << tjob->url();
01586                         wallpaper->setUrls(KUrl::List() << tjob->url());
01587                     }
01588                 } else {
01589                     addApplet(actionsToApplets[choice], args, QRectF(posi, QSize()));
01590                 }
01591 
01592                 clearDataForMimeJob(job);
01593                 return;
01594             }
01595         } else {
01596             // we can at least create an icon as a link to the URL
01597             addApplet("icon", args, QRectF(posi, QSize()));
01598         }
01599     }
01600 
01601     clearDataForMimeJob(job);
01602 #endif // PLASMA_NO_KIO
01603 }
01604 
01605 #ifndef KDE_NO_DEPRECATED
01606 const QGraphicsItem *Containment::toolBoxItem() const
01607 {
01608     return d->toolBox.data();
01609 }
01610 #endif
01611 
01612 void Containment::setToolBox(AbstractToolBox *toolBox)
01613 {
01614     if (d->toolBox.data()) {
01615         d->toolBox.data()->deleteLater();
01616     }
01617     d->toolBox = toolBox;
01618 }
01619 
01620 AbstractToolBox *Containment::toolBox() const
01621 {
01622     return d->toolBox.data();
01623 }
01624 
01625 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event)
01626 {
01627     Applet::resizeEvent(event);
01628 
01629     if (isContainment()) {
01630         if (d->isPanelContainment()) {
01631             d->positionPanel();
01632         } else if (corona()) {
01633             QMetaObject::invokeMethod(corona(), "layoutContainments");
01634         }
01635 
01636         if (d->wallpaper) {
01637             d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
01638         }
01639     }
01640 }
01641 
01642 void Containment::keyPressEvent(QKeyEvent *event)
01643 {
01644     //kDebug() << "keyPressEvent with" << event->key()
01645     //         << "and hoping and wishing for a" << Qt::Key_Tab;
01646     if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) {
01647         if (!d->applets.isEmpty()) {
01648             kDebug() << "let's give focus to...." << (QObject*)d->applets.first();
01649             d->applets.first()->setFocus(Qt::TabFocusReason);
01650         }
01651     }
01652 }
01653 
01654 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event)
01655 {
01656     event->ignore();
01657     if (d->appletAt(event->scenePos())) {
01658         return; //no unexpected click-throughs
01659     }
01660 
01661     if (d->wallpaper && d->wallpaper->isInitialized()) {
01662         QGraphicsItem *item = scene()->itemAt(event->scenePos());
01663         if (item == this) {
01664             event->ignore();
01665             d->wallpaper->wheelEvent(event);
01666 
01667             if (event->isAccepted()) {
01668                 return;
01669             }
01670         }
01671     }
01672 
01673     QString trigger = ContainmentActions::eventToString(event);
01674 
01675     if (d->prepareContainmentActions(trigger, event->screenPos())) {
01676         d->actionPlugins()->value(trigger)->contextEvent(event);
01677         event->accept();
01678     } else {
01679         event->ignore();
01680         Applet::wheelEvent(event);
01681     }
01682 }
01683 
01684 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
01685 {
01686     return Applet::sceneEventFilter(watched, event);
01687 }
01688 
01689 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value)
01690 {
01691     //FIXME if the applet is moved to another containment we need to unfocus it
01692 
01693     if (isContainment() &&
01694         (change == QGraphicsItem::ItemSceneHasChanged ||
01695          change == QGraphicsItem::ItemPositionHasChanged)) {
01696         switch (d->type) {
01697             case PanelContainment:
01698             case CustomPanelContainment:
01699                 d->positionPanel();
01700                 break;
01701             default:
01702                 if (corona()) {
01703                     QMetaObject::invokeMethod(corona(), "layoutContainments");
01704                 }
01705                 break;
01706         }
01707     }
01708 
01709     return Applet::itemChange(change, value);
01710 }
01711 
01712 void Containment::enableAction(const QString &name, bool enable)
01713 {
01714     QAction *action = this->action(name);
01715     if (action) {
01716         action->setEnabled(enable);
01717         action->setVisible(enable);
01718     }
01719 }
01720 
01721 void Containment::addToolBoxAction(QAction *action)
01722 {
01723     d->createToolBox();
01724     if (d->toolBox) {
01725         d->toolBox.data()->addTool(action);
01726     }
01727 }
01728 
01729 void Containment::removeToolBoxAction(QAction *action)
01730 {
01731     if (d->toolBox) {
01732         d->toolBox.data()->removeTool(action);
01733     }
01734 }
01735 
01736 void Containment::setToolBoxOpen(bool open)
01737 {
01738     if (open) {
01739         openToolBox();
01740     } else {
01741         closeToolBox();
01742     }
01743 }
01744 
01745 bool Containment::isToolBoxOpen() const
01746 {
01747     return (d->toolBox && d->toolBox.data()->isShowing());
01748 }
01749 
01750 void Containment::openToolBox()
01751 {
01752     if (d->toolBox && !d->toolBox.data()->isShowing()) {
01753         d->toolBox.data()->setShowing(true);
01754         emit toolBoxVisibilityChanged(true);
01755     }
01756 }
01757 
01758 void Containment::closeToolBox()
01759 {
01760     if (d->toolBox && d->toolBox.data()->isShowing()) {
01761         d->toolBox.data()->setShowing(false);
01762         emit toolBoxVisibilityChanged(false);
01763     }
01764 }
01765 
01766 void Containment::addAssociatedWidget(QWidget *widget)
01767 {
01768     Applet::addAssociatedWidget(widget);
01769     if (d->focusedApplet) {
01770         d->focusedApplet->addAssociatedWidget(widget);
01771     }
01772 
01773     foreach (const Applet *applet, d->applets) {
01774         if (applet->d->activationAction) {
01775             widget->addAction(applet->d->activationAction);
01776         }
01777     }
01778 }
01779 
01780 void Containment::removeAssociatedWidget(QWidget *widget)
01781 {
01782     Applet::removeAssociatedWidget(widget);
01783     if (d->focusedApplet) {
01784         d->focusedApplet->removeAssociatedWidget(widget);
01785     }
01786 
01787     foreach (const Applet *applet, d->applets) {
01788         if (applet->d->activationAction) {
01789             widget->removeAction(applet->d->activationAction);
01790         }
01791     }
01792 }
01793 
01794 void Containment::setDrawWallpaper(bool drawWallpaper)
01795 {
01796     d->drawWallpaper = drawWallpaper;
01797     if (drawWallpaper) {
01798         KConfigGroup cfg = config();
01799         const QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper);
01800         const QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode);
01801         setWallpaper(wallpaper, mode);
01802     } else {
01803         delete d->wallpaper;
01804         d->wallpaper = 0;
01805     }
01806 }
01807 
01808 bool Containment::drawWallpaper()
01809 {
01810     return d->drawWallpaper;
01811 }
01812 
01813 void Containment::setWallpaper(const QString &pluginName, const QString &mode)
01814 {
01815     KConfigGroup cfg = config();
01816     bool newPlugin = true;
01817     bool newMode = true;
01818 
01819     if (d->drawWallpaper) {
01820         if (d->wallpaper) {
01821             // we have a wallpaper, so let's decide whether we need to swap it out
01822             if (d->wallpaper->pluginName() != pluginName) {
01823                 delete d->wallpaper;
01824                 d->wallpaper = 0;
01825             } else {
01826                 // it's the same plugin, so let's save its state now so when
01827                 // we call restore later on we're safe
01828                 newMode = d->wallpaper->renderingMode().name() != mode;
01829                 newPlugin = false;
01830             }
01831         }
01832 
01833         if (!pluginName.isEmpty() && !d->wallpaper) {
01834             d->wallpaper = Plasma::Wallpaper::load(pluginName);
01835         }
01836 
01837         if (d->wallpaper) {
01838             d->wallpaper->setParent(this);
01839             d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
01840             d->wallpaper->setRenderingMode(mode);
01841 
01842             if (newPlugin) {
01843                 cfg.writeEntry("wallpaperplugin", pluginName);
01844             }
01845 
01846             if (d->wallpaper->isInitialized()) {
01847                 KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper");
01848                 wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName);
01849                 d->wallpaper->restore(wallpaperConfig);
01850             }
01851 
01852             if (newMode) {
01853                 cfg.writeEntry("wallpaperpluginmode", mode);
01854             }
01855         }
01856 
01857         update();
01858     }
01859 
01860     if (!d->wallpaper) {
01861         cfg.deleteEntry("wallpaperplugin");
01862         cfg.deleteEntry("wallpaperpluginmode");
01863     }
01864 
01865     if (newPlugin || newMode) {
01866         if (newPlugin && d->wallpaper) {
01867             connect(d->wallpaper, SIGNAL(configureRequested()), this, SLOT(requestConfiguration()));
01868             connect(d->wallpaper, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
01869         }
01870 
01871         emit configNeedsSaving();
01872     }
01873 }
01874 
01875 Plasma::Wallpaper *Containment::wallpaper() const
01876 {
01877     return d->wallpaper;
01878 }
01879 
01880 void Containment::setContainmentActions(const QString &trigger, const QString &pluginName)
01881 {
01882     KConfigGroup cfg = containmentActionsConfig();
01883     ContainmentActions *plugin = 0;
01884 
01885     if (d->actionPlugins()->contains(trigger)) {
01886         plugin = d->actionPlugins()->value(trigger);
01887         if (plugin->pluginName() != pluginName) {
01888             d->actionPlugins()->remove(trigger);
01889             delete plugin;
01890             plugin=0;
01891         }
01892     }
01893     if (pluginName.isEmpty()) {
01894         cfg.deleteEntry(trigger);
01895     } else if (plugin) {
01896         //it already existed, just reload config
01897         if (plugin->isInitialized()) {
01898             plugin->setContainment(this); //to be safe
01899             //FIXME make a truly unique config group
01900             KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
01901             plugin->restore(pluginConfig);
01902         }
01903     } else {
01904         switch (d->containmentActionsSource) {
01905         case ContainmentPrivate::Activity:
01906             //FIXME
01907         case ContainmentPrivate::Local:
01908             plugin = ContainmentActions::load(this, pluginName);
01909             break;
01910         default:
01911             plugin = ContainmentActions::load(0, pluginName);
01912         }
01913         if (plugin) {
01914             cfg.writeEntry(trigger, pluginName);
01915             d->actionPlugins()->insert(trigger, plugin);
01916         } else {
01917             //bad plugin... gets removed. is this a feature or a bug?
01918             cfg.deleteEntry(trigger);
01919         }
01920     }
01921 
01922     emit configNeedsSaving();
01923 }
01924 
01925 QStringList Containment::containmentActionsTriggers()
01926 {
01927     return d->actionPlugins()->keys();
01928 }
01929 
01930 QString Containment::containmentActions(const QString &trigger)
01931 {
01932     ContainmentActions *c = d->actionPlugins()->value(trigger);
01933     return c ? c->pluginName() : QString();
01934 }
01935 
01936 void Containment::setActivity(const QString &activity)
01937 {
01938     Context *context = d->context();
01939     if (context->currentActivity() != activity) {
01940         context->setCurrentActivity(activity);
01941     }
01942 }
01943 
01944 void ContainmentPrivate::onContextChanged(Plasma::Context *con)
01945 {
01946     foreach (Applet *a, applets) {
01947         a->updateConstraints(ContextConstraint);
01948     }
01949 
01950     KConfigGroup c = q->config();
01951     QString act = con->currentActivityId();
01952 
01953     //save anything that's been set (boy I hope this avoids overwriting things)
01954     //FIXME of course if the user sets the name to an empty string we have a bug
01955     //but once we get context retrieving the name as soon as the id is set, this issue should go away
01956     if (!act.isEmpty()) {
01957         c.writeEntry("activityId", act);
01958     }
01959     act = con->currentActivity();
01960     if (!act.isEmpty()) {
01961         c.writeEntry("activity", act);
01962     }
01963 
01964     if (toolBox) {
01965         toolBox.data()->update();
01966     }
01967     emit q->configNeedsSaving();
01968     emit q->contextChanged(con);
01969 }
01970 
01971 QString Containment::activity() const
01972 {
01973     return d->context()->currentActivity();
01974 }
01975 
01976 Context *Containment::context() const
01977 {
01978     return d->context();
01979 }
01980 
01981 Context *ContainmentPrivate::context()
01982 {
01983     if (!con) {
01984         con = new Context(q);
01985         q->connect(con, SIGNAL(changed(Plasma::Context*)),
01986                    q, SLOT(onContextChanged(Plasma::Context*)));
01987     }
01988 
01989     return con;
01990 }
01991 
01992 KActionCollection* ContainmentPrivate::actions()
01993 {
01994     return static_cast<Applet*>(q)->d->actions;
01995 }
01996 
01997 void ContainmentPrivate::focusApplet(Plasma::Applet *applet)
01998 {
01999     if (focusedApplet == applet) {
02000         return;
02001     }
02002 
02003     QList<QWidget *> widgets = actions()->associatedWidgets();
02004     if (focusedApplet) {
02005         foreach (QWidget *w, widgets) {
02006             focusedApplet->removeAssociatedWidget(w);
02007         }
02008     }
02009 
02010     if (applet && applets.contains(applet)) {
02011         //kDebug() << "switching to" << applet->name();
02012         focusedApplet = applet;
02013         foreach (QWidget *w, widgets) {
02014             focusedApplet->addAssociatedWidget(w);
02015         }
02016 
02017         if (!focusedApplet->hasFocus()) {
02018             focusedApplet->setFocus(Qt::ShortcutFocusReason);
02019         }
02020     } else {
02021         focusedApplet = 0;
02022     }
02023 }
02024 
02025 void Containment::focusNextApplet()
02026 {
02027     if (d->applets.isEmpty()) {
02028         return;
02029     }
02030     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
02031     if (index >= d->applets.size()) {
02032         index = 0;
02033     }
02034     kDebug() << "index" << index;
02035     d->focusApplet(d->applets.at(index));
02036 }
02037 
02038 void Containment::focusPreviousApplet()
02039 {
02040     if (d->applets.isEmpty()) {
02041         return;
02042     }
02043     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
02044     if (index < 0) {
02045         index = d->applets.size() - 1;
02046     }
02047     kDebug() << "index" << index;
02048     d->focusApplet(d->applets.at(index));
02049 }
02050 
02051 void Containment::destroy()
02052 {
02053     destroy(true);
02054 }
02055 
02056 void Containment::showConfigurationInterface()
02057 {
02058     Applet::showConfigurationInterface();
02059 }
02060 
02061 void Containment::configChanged()
02062 {
02063 }
02064 
02065 void ContainmentPrivate::configChanged()
02066 {
02067     if (drawWallpaper) {
02068         KConfigGroup group = q->config();
02069         q->setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
02070                         group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
02071     }
02072 }
02073 
02074 void ContainmentPrivate::requestConfiguration()
02075 {
02076     emit q->configureRequested(q);
02077 }
02078 
02079 void ContainmentPrivate::checkStatus(Plasma::ItemStatus appletStatus)
02080 {
02081     //kDebug() << "================== "<< appletStatus << q->status();
02082     if (appletStatus == q->status()) {
02083         emit q->newStatus(appletStatus);
02084         return;
02085     }
02086 
02087     if (appletStatus < q->status()) {
02088         // check to see if any other applet has a higher status, and stick with that
02089         // if we do
02090         foreach (Applet *applet, applets) {
02091             if (applet->status() > appletStatus) {
02092                 appletStatus = applet->status();
02093             }
02094         }
02095     }
02096 
02097     q->setStatus(appletStatus);
02098 }
02099 
02100 void Containment::destroy(bool confirm)
02101 {
02102     if (immutability() != Mutable || Applet::d->transient) {
02103         return;
02104     }
02105 
02106     if (isContainment() && confirm) {
02107         //FIXME: should not be blocking
02108         const QString title = i18nc("@title:window %1 is the name of the containment", "Remove %1", name());
02109         KGuiItem remove = KStandardGuiItem::remove();
02110         remove.setText(title);
02111         if (KMessageBox::warningContinueCancel(view(),
02112             i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()),
02113             title, remove) != KMessageBox::Continue) {
02114             return;
02115         }
02116     }
02117 
02118     Applet::destroy();
02119 }
02120 
02121 void ContainmentPrivate::createToolBox()
02122 {
02123     if (!toolBox && KAuthorized::authorizeKAction("plasma/containment_context_menu")) {
02124         toolBox = Plasma::AbstractToolBox::load(q->corona()->preferredToolBoxPlugin(type), QVariantList(), q);
02125 
02126         if (toolBox) {
02127             QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
02128             QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SLOT(updateToolBoxVisibility()));
02129 
02130             positionToolBox();
02131         }
02132     }
02133 }
02134 
02135 void ContainmentPrivate::positionToolBox()
02136 {
02137     QMetaObject::invokeMethod(toolBox.data(), "reposition");
02138 }
02139 
02140 void ContainmentPrivate::updateToolBoxVisibility()
02141 {
02142     emit q->toolBoxVisibilityChanged(toolBox.data()->isShowing());
02143 }
02144 
02145 void ContainmentPrivate::triggerShowAddWidgets()
02146 {
02147     emit q->showAddWidgetsInterface(QPointF());
02148 }
02149 
02150 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
02151 {
02152     if (!q->isContainment()) {
02153         return;
02154     }
02155 
02156     //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox;
02157     if (constraints & Plasma::ImmutableConstraint) {
02158         //update actions
02159         checkRemoveAction();
02160         const bool unlocked = q->immutability() == Mutable;
02161         q->setAcceptDrops(unlocked);
02162         q->enableAction("add widgets", unlocked);
02163 
02164         // tell the applets too
02165         foreach (Applet *a, applets) {
02166             a->setImmutability(q->immutability());
02167             a->updateConstraints(ImmutableConstraint);
02168         }
02169     }
02170 
02171     // pass on the constraints that are relevant here
02172     Constraints appletConstraints = NoConstraint;
02173     if (constraints & FormFactorConstraint) {
02174         appletConstraints |= FormFactorConstraint;
02175     }
02176 
02177     if (constraints & ScreenConstraint) {
02178         appletConstraints |= ScreenConstraint;
02179     }
02180 
02181     if (appletConstraints != NoConstraint) {
02182         foreach (Applet *applet, applets) {
02183             applet->updateConstraints(appletConstraints);
02184         }
02185     }
02186 
02187     if (toolBox && (constraints & Plasma::SizeConstraint ||
02188                     constraints & Plasma::FormFactorConstraint ||
02189                     constraints & Plasma::ScreenConstraint ||
02190                     constraints & Plasma::StartupCompletedConstraint)) {
02191         //kDebug() << "Positioning toolbox";
02192         positionToolBox();
02193     }
02194 
02195     if (constraints & Plasma::StartupCompletedConstraint && type < Containment::CustomContainment) {
02196         q->addToolBoxAction(q->action("remove"));
02197         checkRemoveAction();
02198     }
02199 }
02200 
02201 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args,
02202                                       const QRectF &appletGeometry, uint id, bool delayInit)
02203 {
02204     if (!q->isContainment()) {
02205         return 0;
02206     }
02207 
02208     if (!delayInit && q->immutability() != Mutable) {
02209         kDebug() << "addApplet for" << name << "requested, but we're currently immutable!";
02210         return 0;
02211     }
02212 
02213     QGraphicsView *v = q->view();
02214     if (v) {
02215         v->setCursor(Qt::BusyCursor);
02216     }
02217 
02218     Applet *applet = Applet::load(name, id, args);
02219     if (v) {
02220         v->unsetCursor();
02221     }
02222 
02223     if (!applet) {
02224         kDebug() << "Applet" << name << "could not be loaded.";
02225         applet = new Applet(0, QString(), id);
02226         applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name));
02227     }
02228 
02229     //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry();
02230 
02231     q->addApplet(applet, appletGeometry.topLeft(), delayInit);
02232     return applet;
02233 }
02234 
02235 bool ContainmentPrivate::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) const
02236 {
02237     foreach (Applet *applet, applets) {
02238         if (applet != ignoredApplet && applet->geometry().intersects(region)) {
02239             return false;
02240         }
02241     }
02242     return true;
02243 }
02244 
02245 void ContainmentPrivate::appletDestroyed(Plasma::Applet *applet)
02246 {
02247     applets.removeAll(applet);
02248     if (focusedApplet == applet) {
02249         focusedApplet = 0;
02250     }
02251 
02252     emit q->appletRemoved(applet);
02253     emit q->configNeedsSaving();
02254 }
02255 
02256 void ContainmentPrivate::appletAppearAnimationComplete()
02257 {
02258     Animation *anim = qobject_cast<Animation *>(q->sender());
02259     if (anim) {
02260         Applet *applet = qobject_cast<Applet*>(anim->targetWidget());
02261         if (applet) {
02262             appletAppeared(applet);
02263         }
02264     }
02265 }
02266 
02267 void ContainmentPrivate::appletAppeared(Applet *applet)
02268 {
02269     //kDebug() << type << Containment::DesktopContainment;
02270     KConfigGroup *cg = applet->d->mainConfigGroup();
02271     applet->save(*cg);
02272     emit q->configNeedsSaving();
02273 }
02274 
02275 void ContainmentPrivate::positionPanel(bool force)
02276 {
02277     if (!q->scene()) {
02278         kDebug() << "no scene yet";
02279         return;
02280     }
02281 
02282     // already positioning the panel - avoid infinite loops
02283     if (ContainmentPrivate::s_positioningPanels) {
02284         return;
02285     }
02286 
02287     // we position panels in negative coordinates, and stack all horizontal
02288     // and all vertical panels with each other.
02289 
02290 
02291     const QPointF p = q->pos();
02292 
02293     if (!force &&
02294         p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
02295         q->scene()->collidingItems(q).isEmpty()) {
02296         // already positioned and not running into any other panels
02297         return;
02298     }
02299 
02300 
02301     QPointF newPos = preferredPanelPos(q->corona());
02302     if (p != newPos) {
02303         ContainmentPrivate::s_positioningPanels = true;
02304         q->setPos(newPos);
02305         ContainmentPrivate::s_positioningPanels = false;
02306     }
02307 }
02308 
02309 bool ContainmentPrivate::isPanelContainment() const
02310 {
02311     return type == Containment::PanelContainment || type == Containment::CustomPanelContainment;
02312 }
02313 
02314 QPointF ContainmentPrivate::preferredPos(Corona *corona) const
02315 {
02316     Q_ASSERT(corona);
02317 
02318     if (isPanelContainment()) {
02319         //kDebug() << "is a panel, so put it at" << preferredPanelPos(corona);
02320         return preferredPanelPos(corona);
02321     }
02322 
02323     QPointF pos(0, 0);
02324     QTransform t;
02325     while (QGraphicsItem *i = corona->itemAt(pos, t)) {
02326         pos.setX(i->scenePos().x() + i->boundingRect().width() + 10);
02327     }
02328 
02329     //kDebug() << "not a panel, put it at" << pos;
02330     return pos;
02331 }
02332 
02333 QPointF ContainmentPrivate::preferredPanelPos(Corona *corona) const
02334 {
02335     Q_ASSERT(corona);
02336 
02337     //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here
02338     bool horiz = formFactor == Plasma::Horizontal;
02339     qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
02340     qreal lastHeight = 0;
02341 
02342     // this should be ok for small numbers of panels, but if we ever end
02343     // up managing hundreds of them, this simplistic alogrithm will
02344     // likely be too slow.
02345     foreach (const Containment *other, corona->containments()) {
02346         if (other == q ||
02347             !other->d->isPanelContainment() ||
02348             horiz != (other->formFactor() == Plasma::Horizontal)) {
02349             // only line up with panels of the same orientation
02350             continue;
02351         }
02352 
02353         if (horiz) {
02354             qreal y = other->pos().y();
02355             if (y < bottom) {
02356                 lastHeight = other->size().height();
02357                 bottom = y;
02358             }
02359         } else {
02360             qreal width = other->size().width();
02361             qreal x = other->pos().x() + width;
02362             if (x > bottom) {
02363                 lastHeight = width;
02364                 bottom = x + lastHeight;
02365             }
02366         }
02367     }
02368 
02369     // give a space equal to the height again of the last item so there is
02370     // room to grow.
02371     QPointF newPos;
02372     if (horiz) {
02373         bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
02374         //TODO: fix x position for non-flush-left panels
02375         kDebug() << "moved to" << QPointF(0, bottom - q->size().height());
02376         newPos = QPointF(0, bottom - q->size().height());
02377     } else {
02378         bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
02379         //TODO: fix y position for non-flush-top panels
02380         kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
02381         newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
02382     }
02383 
02384     return newPos;
02385 }
02386 
02387 
02388 bool ContainmentPrivate::prepareContainmentActions(const QString &trigger, const QPoint &screenPos, KMenu *menu)
02389 {
02390     ContainmentActions *plugin = actionPlugins()->value(trigger);
02391     if (!plugin) {
02392         return false;
02393     }
02394     plugin->setContainment(q);
02395 
02396     if (!plugin->isInitialized()) {
02397         KConfigGroup cfg = q->containmentActionsConfig();
02398         KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
02399         plugin->restore(pluginConfig);
02400     }
02401 
02402     if (plugin->configurationRequired()) {
02403         KMenu *localMenu = menu ? menu : new KMenu();
02404 
02405         localMenu->addTitle(i18n("This plugin needs to be configured"));
02406         localMenu->addAction(q->action("configure"));
02407 
02408         if (!menu) {
02409             localMenu->exec(screenPos);
02410             delete localMenu;
02411         }
02412 
02413         return false;
02414     } else if (menu) {
02415         QList<QAction*> actions = plugin->contextualActions();
02416         if (actions.isEmpty()) {
02417             //it probably didn't bother implementing the function. give the user a chance to set
02418             //a better plugin.  note that if the user sets no-plugin this won't happen...
02419             if (!isPanelContainment() && q->action("configure")) {
02420                 menu->addAction(q->action("configure"));
02421             }
02422         } else {
02423             menu->addActions(actions);
02424         }
02425     }
02426 
02427     return true;
02428 }
02429 
02430 KConfigGroup Containment::containmentActionsConfig()
02431 {
02432     KConfigGroup cfg;
02433     switch (d->containmentActionsSource) {
02434     case ContainmentPrivate::Local:
02435         cfg = config();
02436         cfg = KConfigGroup(&cfg, "ActionPlugins");
02437         break;
02438     case ContainmentPrivate::Activity:
02439         cfg = KConfigGroup(corona()->config(), "Activities");
02440         cfg = KConfigGroup(&cfg, d->context()->currentActivityId());
02441         cfg = KConfigGroup(&cfg, "ActionPlugins");
02442         break;
02443     default:
02444         cfg = KConfigGroup(corona()->config(), "ActionPlugins");
02445     }
02446     return cfg;
02447 }
02448 
02449 QHash<QString, ContainmentActions*> * ContainmentPrivate::actionPlugins()
02450 {
02451     switch (containmentActionsSource) {
02452         case Activity:
02453             //FIXME
02454         case Local:
02455             return &localActionPlugins;
02456         default:
02457             return &globalActionPlugins;
02458     }
02459 }
02460 
02461 } // Plasma namespace
02462 
02463 #include "containment.moc"
02464 
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:51:35 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal