• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • 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/applethandle_p.h"
00071 #include "private/containmentactionspluginsconfig_p.h"
00072 #include "private/extenderitemmimedata_p.h"
00073 #include "private/extenderapplet_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, false);
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 dynamic_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     QAction *configureApplet = applet->d->actions->action("configure");
00691     if (configureApplet && configureApplet->isEnabled()) {
00692         desktopMenu.addAction(configureApplet);
00693     }
00694 
00695     QAction *runAssociatedApplication = applet->d->actions->action("run associated application");
00696     if (runAssociatedApplication && runAssociatedApplication->isEnabled()) {
00697         desktopMenu.addAction(runAssociatedApplication);
00698     }
00699 
00700     KMenu *containmentMenu = new KMenu(i18nc("%1 is the name of the containment", "%1 Options", q->name()), &desktopMenu);
00701     addContainmentActions(*containmentMenu, event);
00702     if (!containmentMenu->isEmpty()) {
00703         int enabled = 0;
00704         //count number of real actions
00705         QListIterator<QAction *> actionsIt(containmentMenu->actions());
00706         while (enabled < 3 && actionsIt.hasNext()) {
00707             QAction *action = actionsIt.next();
00708             if (action->isVisible() && !action->isSeparator()) {
00709                 ++enabled;
00710             }
00711         }
00712 
00713         if (enabled) {
00714             //if there is only one, don't create a submenu
00715             if (enabled < 2) {
00716                 foreach (QAction *action, containmentMenu->actions()) {
00717                     if (action->isVisible() && !action->isSeparator()) {
00718                         desktopMenu.addAction(action);
00719                     }
00720                 }
00721             } else {
00722                 desktopMenu.addMenu(containmentMenu);
00723             }
00724         }
00725     }
00726 
00727     if (q->immutability() == Mutable) {
00728         QAction *closeApplet = applet->d->actions->action("remove");
00729         kDebug() << "checking for removal" << closeApplet;
00730         if (closeApplet) {
00731             if (!desktopMenu.isEmpty()) {
00732                 desktopMenu.addSeparator();
00733             }
00734 
00735             kDebug() << "boo yah, adding it!" << closeApplet->isEnabled() << closeApplet->isVisible();
00736             desktopMenu.addAction(closeApplet);
00737         }
00738     }
00739 }
00740 
00741 Applet* ContainmentPrivate::appletAt(const QPointF &point)
00742 {
00743     Applet *applet = 0;
00744 
00745     QGraphicsItem *item = q->scene()->itemAt(point);
00746     if (item == q) {
00747         item = 0;
00748     }
00749 
00750     while (item) {
00751         if (item->isWidget()) {
00752             applet = qobject_cast<Applet*>(static_cast<QGraphicsWidget*>(item));
00753             if (applet) {
00754                 if (applet->isContainment()) {
00755                     applet = 0;
00756                 }
00757                 break;
00758             }
00759         }
00760         AppletHandle *handle = dynamic_cast<AppletHandle*>(item);
00761         if (handle) {
00762             //pretend it was on the applet
00763             applet = handle->applet();
00764             break;
00765         }
00766         item = item->parentItem();
00767     }
00768     return applet;
00769 }
00770 
00771 void Containment::setFormFactor(FormFactor formFactor)
00772 {
00773     if (d->formFactor == formFactor) {
00774         return;
00775     }
00776 
00777     //kDebug() << "switching FF to " << formFactor;
00778     d->formFactor = formFactor;
00779 
00780     if (isContainment() &&
00781         (d->type == PanelContainment || d->type == CustomPanelContainment)) {
00782         // we are a panel and we have chaged our orientation
00783         d->positionPanel(true);
00784     }
00785 
00786     QMetaObject::invokeMethod(d->toolBox.data(), "reposition");
00787 
00788     updateConstraints(Plasma::FormFactorConstraint);
00789 
00790     KConfigGroup c = config();
00791     c.writeEntry("formfactor", (int)formFactor);
00792     emit configNeedsSaving();
00793 }
00794 
00795 void Containment::setLocation(Location location)
00796 {
00797     if (d->location == location) {
00798         return;
00799     }
00800 
00801     bool emitGeomChange = false;
00802 
00803     if ((location == TopEdge || location == BottomEdge) &&
00804         (d->location == TopEdge || d->location == BottomEdge)) {
00805         emitGeomChange = true;
00806     }
00807 
00808     if ((location == RightEdge || location == LeftEdge) &&
00809         (d->location == RightEdge || d->location == LeftEdge)) {
00810         emitGeomChange = true;
00811     }
00812 
00813     d->location = location;
00814 
00815     foreach (Applet *applet, d->applets) {
00816         applet->updateConstraints(Plasma::LocationConstraint);
00817     }
00818 
00819     if (emitGeomChange) {
00820         // our geometry on the scene will not actually change,
00821         // but for the purposes of views it has
00822         emit geometryChanged();
00823     }
00824 
00825     updateConstraints(Plasma::LocationConstraint);
00826 
00827     KConfigGroup c = config();
00828     c.writeEntry("location", (int)location);
00829     emit configNeedsSaving();
00830 }
00831 
00832 void Containment::addSiblingContainment()
00833 {
00834     emit addSiblingContainment(this);
00835 }
00836 
00837 void Containment::clearApplets()
00838 {
00839     foreach (Applet *applet, d->applets) {
00840         applet->d->cleanUpAndDelete();
00841     }
00842 
00843     d->applets.clear();
00844 }
00845 
00846 Applet *Containment::addApplet(const QString &name, const QVariantList &args,
00847                                const QRectF &appletGeometry)
00848 {
00849     return d->addApplet(name, args, appletGeometry);
00850 }
00851 
00852 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit)
00853 {
00854     if (!isContainment() || (!delayInit && immutability() != Mutable)) {
00855         return;
00856     }
00857 
00858     if (!applet) {
00859         kDebug() << "adding null applet!?!";
00860         return;
00861     }
00862 
00863     if (d->applets.contains(applet)) {
00864         kDebug() << "already have this applet!";
00865     }
00866 
00867     Containment *currentContainment = applet->containment();
00868 
00869     if (d->type == PanelContainment) {
00870         //panels don't want backgrounds, which is important when setting geometry
00871         setBackgroundHints(NoBackground);
00872     }
00873 
00874     if (currentContainment && currentContainment != this) {
00875         emit currentContainment->appletRemoved(applet);
00876         if (currentContainment->d->focusedApplet == applet) {
00877             currentContainment->d->focusedApplet = 0;
00878         }
00879 
00880         disconnect(applet, 0, currentContainment, 0);
00881         applet->removeSceneEventFilter(currentContainment);
00882         KConfigGroup oldConfig = applet->config();
00883         currentContainment->d->applets.removeAll(applet);
00884         if (currentContainment->d->handles.contains(applet)) {
00885             currentContainment->d->handles.remove(applet);
00886         }
00887         applet->setParentItem(this);
00888         applet->setParent(this);
00889 
00890         // now move the old config to the new location
00891         //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc
00892         KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
00893         oldConfig.reparent(&c);
00894         applet->d->resetConfigurationObject();
00895 
00896         disconnect(applet, SIGNAL(activate()), currentContainment, SIGNAL(activate()));
00897     } else {
00898         applet->setParentItem(this);
00899         applet->setParent(this);
00900     }
00901 
00902     d->applets << applet;
00903 
00904     connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
00905     connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus()));
00906     connect(applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed(Plasma::Applet*)));
00907     connect(applet, SIGNAL(newStatus(Plasma::ItemStatus)), this, SLOT(checkStatus(Plasma::ItemStatus)));
00908     connect(applet, SIGNAL(activate()), this, SIGNAL(activate()));
00909 
00910     if (pos != QPointF(-1, -1)) {
00911         applet->setPos(pos);
00912     }
00913 
00914     if (delayInit || currentContainment) {
00915         if (d->type == DesktopContainment) {
00916             applet->installSceneEventFilter(this);
00917             //applet->setWindowFlags(Qt::Window);
00918         }
00919     } else {
00920         applet->restore(*applet->d->mainConfigGroup());
00921         applet->init();
00922         Plasma::Animation *anim = Plasma::Animator::create(Plasma::Animator::AppearAnimation);
00923         if (anim) {
00924             connect(anim, SIGNAL(finished()), this, SLOT(appletAppearAnimationComplete()));
00925             anim->setTargetWidget(applet);
00926             //FIXME: small hack until we have proper js anim support; allows 'zoom' to work in the
00927             //'right' direction for appearance
00928             anim->setDirection(QAbstractAnimation::Backward);
00929             anim->start(QAbstractAnimation::DeleteWhenStopped);
00930         } else {
00931             d->appletAppeared(applet);
00932         }
00933     }
00934 
00935     applet->setFlag(QGraphicsItem::ItemIsMovable, true);
00936     applet->updateConstraints(Plasma::AllConstraints);
00937     if (!delayInit) {
00938         applet->flushPendingConstraintsEvents();
00939     }
00940     emit appletAdded(applet, pos);
00941 
00942     if (!currentContainment) {
00943         applet->updateConstraints(Plasma::StartupCompletedConstraint);
00944         if (!delayInit) {
00945             applet->flushPendingConstraintsEvents();
00946         }
00947     }
00948 
00949     if (!delayInit) {
00950         applet->d->scheduleModificationNotification();
00951     }
00952 }
00953 
00954 Applet::List Containment::applets() const
00955 {
00956     return d->applets;
00957 }
00958 
00959 void Containment::setScreen(int newScreen, int newDesktop)
00960 {
00961     d->setScreen(newScreen, newDesktop);
00962 }
00963 
00964 void ContainmentPrivate::setScreen(int newScreen, int newDesktop, bool preventInvalidDesktops)
00965 {
00966     // What we want to do in here is:
00967     //   * claim the screen as our own
00968     //   * signal whatever may be watching this containment about the switch
00969     //   * if we are a full screen containment, then:
00970     //      * resize to match the screen if we're that kind of containment
00971     //      * kick other full-screen containments off this screen
00972     //          * if we had a screen, then give our screen to the containment
00973     //            we kick out
00974     //
00975     // a screen of -1 means no associated screen.
00976     Corona *corona = q->corona();
00977     Q_ASSERT(corona);
00978 
00979     //if it's an offscreen widget, don't allow to claim a screen, after all it's *off*screen
00980     if (corona->offscreenWidgets().contains(q)) {
00981         return;
00982     }
00983 
00984     int numScreens = corona->numScreens();
00985     if (newScreen < -1) {
00986         newScreen = -1;
00987     }
00988 
00989     // -1 == All desktops
00990     if (newDesktop < -1 || (preventInvalidDesktops && newDesktop > KWindowSystem::numberOfDesktops() - 1)) {
00991         newDesktop = -1;
00992     }
00993 
00994     //kDebug() << activity() << "setting screen to " << newScreen << newDesktop << "and type is" << type;
00995 
00996     Containment *swapScreensWith(0);
00997     if (type == Containment::DesktopContainment || type >= Containment::CustomContainment) {
00998         // we want to listen to changes in work area if our screen changes
00999         if (toolBox) {
01000             if (screen < 0 && newScreen > -1) {
01001                 QObject::connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()), Qt::UniqueConnection);
01002             } else if (newScreen < 0) {
01003                 QObject::disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), toolBox.data(), SLOT(reposition()));
01004             }
01005         }
01006 
01007         if (newScreen > -1) {
01008             // sanity check to make sure someone else doesn't have this screen already!
01009             Containment *currently = corona->containmentForScreen(newScreen, newDesktop);
01010             if (currently && currently != q) {
01011                 kDebug() << "currently is on screen" << currently->screen()
01012                          << "desktop" << currently->desktop()
01013                          << "and is" << currently->activity()
01014                          << (QObject*)currently << "i'm" << (QObject*)q;
01015                 currently->setScreen(-1, currently->desktop());
01016                 swapScreensWith = currently;
01017             }
01018         }
01019     }
01020 
01021     if (newScreen < numScreens && newScreen > -1 &&
01022         (type == Containment::DesktopContainment || type >= Containment::CustomContainment)) {
01023         q->resize(corona->screenGeometry(newScreen).size());
01024     }
01025 
01026     int oldDesktop = desktop;
01027     desktop = newDesktop;
01028 
01029     int oldScreen = screen;
01030     screen = newScreen;
01031 
01032     q->updateConstraints(Plasma::ScreenConstraint);
01033 
01034     if (oldScreen != newScreen || oldDesktop != newDesktop) {
01035         /*
01036         kDebug() << "going to signal change for" << q
01037                  << ", old screen & desktop:" << oldScreen << oldDesktop
01038                  << ", new:" << screen << desktop;
01039                  */
01040         KConfigGroup c = q->config();
01041         c.writeEntry("screen", screen);
01042         c.writeEntry("desktop", desktop);
01043         if (newScreen != -1) {
01044             lastScreen = newScreen;
01045             lastDesktop = newDesktop;
01046             c.writeEntry("lastScreen", lastScreen);
01047             c.writeEntry("lastDesktop", lastDesktop);
01048         }
01049         emit q->configNeedsSaving();
01050         emit q->screenChanged(oldScreen, newScreen, q);
01051     }
01052 
01053     if (swapScreensWith) {
01054         //kDebug() << "setScreen due to swap, part 2";
01055         swapScreensWith->setScreen(oldScreen, oldDesktop);
01056     }
01057 
01058     checkRemoveAction();
01059 
01060     if (newScreen >= 0) {
01061         emit q->activate();
01062     }
01063 }
01064 
01065 int Containment::screen() const
01066 {
01067     return d->screen;
01068 }
01069 
01070 int Containment::lastScreen() const
01071 {
01072     return d->lastScreen;
01073 }
01074 
01075 int Containment::desktop() const
01076 {
01077     return d->desktop;
01078 }
01079 
01080 int Containment::lastDesktop() const
01081 {
01082     return d->lastDesktop;
01083 }
01084 
01085 KPluginInfo::List Containment::listContainments(const QString &category,
01086                                                 const QString &parentApp)
01087 {
01088     return listContainmentsOfType(QString(), category, parentApp);
01089 }
01090 
01091 
01092 KPluginInfo::List Containment::listContainmentsOfType(const QString &type,
01093                                                       const QString &category,
01094                                                       const QString &parentApp)
01095 {
01096     QString constraint;
01097 
01098     if (parentApp.isEmpty()) {
01099         constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')");
01100     } else {
01101         constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
01102     }
01103 
01104     if (!type.isEmpty()) {
01105         if (!constraint.isEmpty()) {
01106             constraint.append(" and ");
01107         }
01108 
01109         constraint.append("'").append(type).append("' ~in [X-Plasma-ContainmentCategories]");
01110     }
01111 
01112     if (!category.isEmpty()) {
01113         if (!constraint.isEmpty()) {
01114             constraint.append(" and ");
01115         }
01116 
01117         constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
01118         if (category == "Miscellaneous") {
01119             constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
01120         }
01121     }
01122 
01123     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
01124     //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches";
01125     return KPluginInfo::fromServices(offers);
01126 }
01127 
01128 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype)
01129 {
01130     const QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
01131     //kDebug() << mimetype << constraint;
01132     const KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
01133     return KPluginInfo::fromServices(offers);
01134 }
01135 
01136 QStringList Containment::listContainmentTypes()
01137 {
01138     KPluginInfo::List containmentInfos = listContainments();
01139     QSet<QString> types;
01140 
01141     foreach (const KPluginInfo &containmentInfo, containmentInfos) {
01142         QStringList theseTypes = containmentInfo.service()->property("X-Plasma-ContainmentCategories").toStringList();
01143         foreach (const QString &type, theseTypes) {
01144             types.insert(type);
01145         }
01146     }
01147 
01148     return types.toList();
01149 }
01150 
01151 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
01152 {
01153     //kDebug() << immutability() << Mutable << (immutability() == Mutable);
01154     event->setAccepted(immutability() == Mutable &&
01155                        (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) ||
01156                         KUrl::List::canDecode(event->mimeData()) ||
01157                         event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())));
01158 
01159     if (!event->isAccepted()) {
01160         // check to see if we have an applet that accepts the format.
01161         QStringList formats = event->mimeData()->formats();
01162 
01163         foreach (const QString &format, formats) {
01164             KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format);
01165             if (!appletList.isEmpty()) {
01166                 event->setAccepted(true);
01167                 break;
01168             }
01169         }
01170 
01171         if (!event->isAccepted()) {
01172             foreach (const QString &format, formats) {
01173                 KPluginInfo::List wallpaperList = Wallpaper::listWallpaperInfoForMimetype(format);
01174                 if (!wallpaperList.isEmpty()) {
01175                     event->setAccepted(true);
01176                     break;
01177                 }
01178             }
01179         }
01180     }
01181 
01182     if (event->isAccepted()) {
01183         if (d->dropZoneStarted) {
01184             showDropZone(event->pos().toPoint());
01185         } else {
01186             if (!d->showDropZoneDelayTimer) {
01187                 d->showDropZoneDelayTimer = new QTimer(this);
01188                 d->showDropZoneDelayTimer->setInterval(300);
01189                 d->showDropZoneDelayTimer->setSingleShot(true);
01190                 connect(d->showDropZoneDelayTimer, SIGNAL(timeout()), this, SLOT(showDropZoneDelayed()));
01191             }
01192 
01193             d->dropPoints.insert(0, event->pos());
01194             d->showDropZoneDelayTimer->start();
01195         }
01196     }
01197 }
01198 
01199 void Containment::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
01200 {
01201     //kDebug() << event->pos() << size().height() << size().width();
01202     if (d->showDropZoneDelayTimer) {
01203         d->showDropZoneDelayTimer->stop();
01204     }
01205 
01206     if (event->pos().y() < 1 || event->pos().y() > size().height() ||
01207         event->pos().x() < 1 || event->pos().x() > size().width()) {
01208         showDropZone(QPoint());
01209         d->dropZoneStarted = false;
01210     }
01211 }
01212 
01213 void ContainmentPrivate::showDropZoneDelayed()
01214 {
01215     dropZoneStarted = true;
01216     q->showDropZone(dropPoints.value(0).toPoint());
01217     dropPoints.remove(0);
01218 }
01219 
01220 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
01221 {
01222     QGraphicsItem *item = scene()->itemAt(event->scenePos());
01223     event->setAccepted(item == this || item == d->toolBox.data() || !item);
01224     //kDebug() << event->isAccepted() << d->showDropZoneDelayTimer->isActive();
01225     if (!event->isAccepted()) {
01226         if (d->showDropZoneDelayTimer) {
01227             d->showDropZoneDelayTimer->stop();
01228         }
01229     } else if (!d->showDropZoneDelayTimer->isActive() && immutability() == Plasma::Mutable) {
01230         showDropZone(event->pos().toPoint());
01231     }
01232 }
01233 
01234 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event)
01235 {
01236     if (isContainment()) {
01237         d->dropData(event->scenePos(), event->screenPos(), event);
01238     } else {
01239         Applet::dropEvent(event);
01240     }
01241 }
01242 
01243 void ContainmentPrivate::dropData(QPointF scenePos, QPoint screenPos, QGraphicsSceneDragDropEvent *dropEvent)
01244 {
01245     if (q->immutability() != Mutable) {
01246         return;
01247     }
01248 
01249     QPointF pos = q->mapFromScene(scenePos);
01250     const QMimeData *mimeData = 0;
01251 
01252     if (dropEvent) {
01253         mimeData = dropEvent->mimeData();
01254     } else {
01255         QClipboard *clipboard = QApplication::clipboard();
01256         mimeData = clipboard->mimeData(QClipboard::Selection);
01257         //TODO if that's not supported (ie non-linux) should we try clipboard instead of selection?
01258     }
01259 
01260     if (!mimeData) {
01261         //Selection is either empty or not supported on this OS
01262         kDebug() << "no mime data";
01263         return;
01264     }
01265 
01266     //kDebug() << event->mimeData()->text();
01267 
01268     QString appletMimetype(q->corona() ? q->corona()->appletMimeType() : QString());
01269 
01270     if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
01271         QString data = mimeData->data(appletMimetype);
01272         const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
01273         foreach (const QString &appletName, appletNames) {
01274             //kDebug() << "doing" << appletName;
01275             QRectF geom(pos, QSize(0, 0));
01276             q->addApplet(appletName, QVariantList(), geom);
01277         }
01278         if (dropEvent) {
01279             dropEvent->acceptProposedAction();
01280         }
01281     } else if (mimeData->hasFormat(ExtenderItemMimeData::mimeType())) {
01282         kDebug() << "mimetype plasma/extenderitem is dropped, creating internal:extender";
01283         //Handle dropping extenderitems.
01284         const ExtenderItemMimeData *extenderData = qobject_cast<const ExtenderItemMimeData*>(mimeData);
01285         if (extenderData) {
01286             ExtenderItem *item = extenderData->extenderItem();
01287             QRectF geometry(pos - extenderData->pointerOffset(), item->size());
01288             kDebug() << "desired geometry: " << geometry;
01289             Applet *applet = qobject_cast<ExtenderApplet *>(item->extender() ?  item->extender()->applet() : 0);
01290             if (applet) {
01291                 qreal left, top, right, bottom;
01292                 applet->getContentsMargins(&left, &top, &right, &bottom);
01293                 applet->setPos(geometry.topLeft() - QPointF(int(left), int(top)));
01294                 applet->show();
01295             } else {
01296                 applet = addApplet("internal:extender", QVariantList(), geometry, 0, true);
01297                 applet->hide();
01298                 applet->init();
01299                 appletAppeared(applet);
01300                 applet->flushPendingConstraintsEvents();
01301                 applet->d->scheduleModificationNotification();
01302                 applet->adjustSize();
01303                 applet->show();
01304             }
01305             item->setExtender(applet->extender());
01306         }
01307     } else if (KUrl::List::canDecode(mimeData)) {
01308         //TODO: collect the mimetypes of available script engines and offer
01309         //      to create widgets out of the matching URLs, if any
01310         const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
01311         foreach (const KUrl &url, urls) {
01312             if (AccessManager::supportedProtocols().contains(url.protocol())) {
01313                 AccessAppletJob *job = AccessManager::self()->accessRemoteApplet(url);
01314                 if (dropEvent) {
01315                     dropPoints[job] = dropEvent->pos();
01316                 } else {
01317                     dropPoints[job] = scenePos;
01318                 }
01319                 QObject::connect(AccessManager::self(), SIGNAL(finished(Plasma::AccessAppletJob*)),
01320                                  q, SLOT(remoteAppletReady(Plasma::AccessAppletJob*)));
01321             }
01322 #ifndef PLASMA_NO_KIO
01323             else {
01324                 KMimeType::Ptr mime = KMimeType::findByUrl(url);
01325                 QString mimeName = mime->name();
01326                 QRectF geom(pos, QSize());
01327                 QVariantList args;
01328                 args << url.url();
01329                 kDebug() << "can decode" << mimeName << args;
01330 
01331                 // It may be a directory or a file, let's stat
01332                 KIO::JobFlags flags = KIO::HideProgressInfo;
01333                 KIO::MimetypeJob *job = KIO::mimetype(url, flags);
01334                 if (dropEvent) {
01335                     dropPoints[job] = dropEvent->pos();
01336                 } else {
01337                     dropPoints[job] = scenePos;
01338                 }
01339 
01340                 QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(dropJobResult(KJob*)));
01341                 QObject::connect(job, SIGNAL(mimetype(KIO::Job *, const QString&)),
01342                                  q, SLOT(mimeTypeRetrieved(KIO::Job *, const QString&)));
01343 
01344                 KMenu *choices = new KMenu("Content dropped");
01345                 choices->addAction(KIcon("process-working"), i18n("Fetching file type..."));
01346                 if (dropEvent) {
01347                     choices->popup(dropEvent->screenPos());
01348                 } else {
01349                     choices->popup(screenPos);
01350                 }
01351 
01352                 dropMenus[job] = choices;
01353             }
01354 #endif
01355         }
01356 
01357         if (dropEvent) {
01358             dropEvent->acceptProposedAction();
01359         }
01360     } else {
01361         QStringList formats = mimeData->formats();
01362         QHash<QString, KPluginInfo> seenPlugins;
01363         QHash<QString, QString> pluginFormats;
01364 
01365         foreach (const QString &format, formats) {
01366             KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format);
01367 
01368             foreach (const KPluginInfo &plugin, plugins) {
01369                 if (seenPlugins.contains(plugin.pluginName())) {
01370                     continue;
01371                 }
01372 
01373                 seenPlugins.insert(plugin.pluginName(), plugin);
01374                 pluginFormats.insert(plugin.pluginName(), format);
01375             }
01376         }
01377         //kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values();
01378 
01379         QString selectedPlugin;
01380 
01381         if (seenPlugins.isEmpty()) {
01382             // do nothing
01383         } else if (seenPlugins.count() == 1) {
01384             selectedPlugin = seenPlugins.constBegin().key();
01385         } else {
01386             KMenu choices;
01387             QHash<QAction *, QString> actionsToPlugins;
01388             foreach (const KPluginInfo &info, seenPlugins) {
01389                 QAction *action;
01390                 if (!info.icon().isEmpty()) {
01391                     action = choices.addAction(KIcon(info.icon()), info.name());
01392                 } else {
01393                     action = choices.addAction(info.name());
01394                 }
01395 
01396                 actionsToPlugins.insert(action, info.pluginName());
01397             }
01398 
01399             QAction *choice = choices.exec(screenPos);
01400             if (choice) {
01401                 selectedPlugin = actionsToPlugins[choice];
01402             }
01403         }
01404 
01405         if (!selectedPlugin.isEmpty()) {
01406             if (!dropEvent) {
01407                 // since we may have entered an event loop up above with the menu,
01408                 // the clipboard item may no longer be valid, as QClipboard resets
01409                 // the object behind the back of the application with a zero timer
01410                 // so we fetch it again here
01411                 QClipboard *clipboard = QApplication::clipboard();
01412                 mimeData = clipboard->mimeData(QClipboard::Selection);
01413             }
01414 
01415             KTemporaryFile tempFile;
01416             if (mimeData && tempFile.open()) {
01417                 //TODO: what should we do with files after the applet is done with them??
01418                 tempFile.setAutoRemove(false);
01419 
01420                 {
01421                     QDataStream stream(&tempFile);
01422                     QByteArray data = mimeData->data(pluginFormats[selectedPlugin]);
01423                     stream.writeRawData(data, data.size());
01424                 }
01425 
01426                 QRectF geom(pos, QSize());
01427                 QVariantList args;
01428                 args << tempFile.fileName();
01429                 kDebug() << args;
01430                 tempFile.close();
01431 
01432                 q->addApplet(selectedPlugin, args, geom);
01433             }
01434         }
01435     }
01436 }
01437 
01438 void ContainmentPrivate::clearDataForMimeJob(KIO::Job *job)
01439 {
01440 #ifndef PLASMA_NO_KIO
01441     QObject::disconnect(job, 0, q, 0);
01442     dropPoints.remove(job);
01443     KMenu *choices = dropMenus.take(job);
01444     delete choices;
01445     job->kill();
01446 #endif // PLASMA_NO_KIO
01447 }
01448 
01449 void ContainmentPrivate::remoteAppletReady(Plasma::AccessAppletJob *job)
01450 {
01451     QPointF pos = dropPoints.take(job);
01452     if (job->error()) {
01453         //TODO: nice user visible error handling (knotification probably?)
01454         kDebug() << "remote applet access failed: " << job->errorText();
01455         return;
01456     }
01457 
01458     if (!job->applet()) {
01459         kDebug() << "how did we end up here? if applet is null, the job->error should be nonzero";
01460         return;
01461     }
01462 
01463     q->addApplet(job->applet(), pos);
01464 }
01465 
01466 void ContainmentPrivate::dropJobResult(KJob *job)
01467 {
01468 #ifndef PLASMA_NO_KIO
01469     KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
01470     if (!tjob) {
01471         kDebug() << "job is not a KIO::TransferJob, won't handle the drop...";
01472         clearDataForMimeJob(tjob);
01473         return;
01474     }
01475     if (job->error()) {
01476         kDebug() << "ERROR" << tjob->error() << ' ' << tjob->errorString();
01477     }
01478     // We call mimetypeRetrieved since there might be other mechanisms
01479     // for finding suitable applets. Cleanup happens there as well.
01480     mimeTypeRetrieved(qobject_cast<KIO::Job *>(job), QString());
01481 #endif // PLASMA_NO_KIO
01482 }
01483 
01484 void ContainmentPrivate::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype)
01485 {
01486 #ifndef PLASMA_NO_KIO
01487     kDebug() << "Mimetype Job returns." << mimetype;
01488     KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
01489     if (!tjob) {
01490         kDebug() << "job should be a TransferJob, but isn't";
01491         clearDataForMimeJob(job);
01492         return;
01493     }
01494     KPluginInfo::List appletList = Applet::listAppletInfoForUrl(tjob->url());
01495     if (mimetype.isEmpty() && !appletList.count()) {
01496         clearDataForMimeJob(job);
01497         kDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")";
01498         return;
01499     } else {
01500 
01501         QPointF posi; // will be overwritten with the event's position
01502         if (dropPoints.keys().contains(tjob)) {
01503             posi = dropPoints[tjob];
01504             kDebug() << "Received a suitable dropEvent at" << posi;
01505         } else {
01506             kDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob";
01507             clearDataForMimeJob(job);
01508             return;
01509         }
01510 
01511         KMenu *choices = dropMenus.value(tjob);
01512         if (!choices) {
01513             kDebug() << "Bailing out. No QMenu found for this job.";
01514             clearDataForMimeJob(job);
01515             return;
01516         }
01517 
01518         QVariantList args;
01519         args << tjob->url().url() << mimetype;
01520 
01521         kDebug() << "Creating menu for:" << mimetype  << posi << args;
01522 
01523         appletList << Applet::listAppletInfoForMimetype(mimetype);
01524         KPluginInfo::List wallpaperList;
01525         if (q->drawWallpaper()) {
01526             wallpaperList = Wallpaper::listWallpaperInfoForMimetype(mimetype);
01527         }
01528 
01529         if (!appletList.isEmpty() || !wallpaperList.isEmpty()) {
01530             choices->clear();
01531             QHash<QAction *, QString> actionsToApplets;
01532             choices->addTitle(i18n("Widgets"));
01533             foreach (const KPluginInfo &info, appletList) {
01534                 kDebug() << info.name();
01535                 QAction *action;
01536                 if (!info.icon().isEmpty()) {
01537                     action = choices->addAction(KIcon(info.icon()), info.name());
01538                 } else {
01539                     action = choices->addAction(info.name());
01540                 }
01541 
01542                 actionsToApplets.insert(action, info.pluginName());
01543                 kDebug() << info.pluginName();
01544             }
01545             actionsToApplets.insert(choices->addAction(i18n("Icon")), "icon");
01546 
01547             QHash<QAction *, QString> actionsToWallpapers;
01548             if (!wallpaperList.isEmpty())  {
01549                 choices->addTitle(i18n("Wallpaper"));
01550 
01551                 QMap<QString, KPluginInfo> sorted;
01552                 foreach (const KPluginInfo &info, appletList) {
01553                     sorted.insert(info.name(), info);
01554                 }
01555 
01556                 foreach (const KPluginInfo &info, wallpaperList) {
01557                     QAction *action;
01558                     if (!info.icon().isEmpty()) {
01559                         action = choices->addAction(KIcon(info.icon()), info.name());
01560                     } else {
01561                         action = choices->addAction(info.name());
01562                     }
01563 
01564                     actionsToWallpapers.insert(action, info.pluginName());
01565                 }
01566             }
01567 
01568             QAction *choice = choices->exec();
01569             if (choice) {
01570                 // Put the job on hold so it can be recycled to fetch the actual content,
01571                 // which is to be expected when something's dropped onto the desktop and
01572                 // an applet is to be created with this URL
01573                 if (!mimetype.isEmpty() && !tjob->error()) {
01574                     tjob->putOnHold();
01575                     KIO::Scheduler::publishSlaveOnHold();
01576                 }
01577                 QString plugin = actionsToApplets.value(choice);
01578                 if (plugin.isEmpty()) {
01579                     //set wallpapery stuff
01580                     plugin = actionsToWallpapers.value(choice);
01581                     if (!wallpaper || plugin != wallpaper->pluginName()) {
01582                         kDebug() << "Wallpaper dropped:" << tjob->url();
01583                         q->setWallpaper(plugin);
01584                     }
01585 
01586                     if (wallpaper) {
01587                         kDebug() << "Wallpaper dropped:" << tjob->url();
01588                         emit wallpaper->urlDropped(tjob->url());
01589                     }
01590                 } else {
01591                     addApplet(actionsToApplets[choice], args, QRectF(posi, QSize()));
01592                 }
01593 
01594                 clearDataForMimeJob(job);
01595                 return;
01596             }
01597         } else {
01598             // we can at least create an icon as a link to the URL
01599             addApplet("icon", args, QRectF(posi, QSize()));
01600         }
01601     }
01602 
01603     clearDataForMimeJob(job);
01604 #endif // PLASMA_NO_KIO
01605 }
01606 
01607 #ifndef KDE_NO_DEPRECATED
01608 const QGraphicsItem *Containment::toolBoxItem() const
01609 {
01610     return d->toolBox.data();
01611 }
01612 #endif
01613 
01614 void Containment::setToolBox(AbstractToolBox *toolBox)
01615 {
01616     if (d->toolBox.data()) {
01617         d->toolBox.data()->deleteLater();
01618     }
01619     d->toolBox = toolBox;
01620 }
01621 
01622 AbstractToolBox *Containment::toolBox() const
01623 {
01624     return d->toolBox.data();
01625 }
01626 
01627 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event)
01628 {
01629     Applet::resizeEvent(event);
01630 
01631     if (isContainment()) {
01632         if (d->isPanelContainment()) {
01633             d->positionPanel();
01634         } else if (corona()) {
01635             QMetaObject::invokeMethod(corona(), "layoutContainments");
01636         }
01637 
01638         if (d->wallpaper) {
01639             d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
01640         }
01641     }
01642 }
01643 
01644 void Containment::keyPressEvent(QKeyEvent *event)
01645 {
01646     //kDebug() << "keyPressEvent with" << event->key()
01647     //         << "and hoping and wishing for a" << Qt::Key_Tab;
01648     if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) {
01649         if (!d->applets.isEmpty()) {
01650             kDebug() << "let's give focus to...." << (QObject*)d->applets.first();
01651             d->applets.first()->setFocus(Qt::TabFocusReason);
01652         }
01653     }
01654 }
01655 
01656 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event)
01657 {
01658     event->ignore();
01659     if (d->appletAt(event->scenePos())) {
01660         return; //no unexpected click-throughs
01661     }
01662 
01663     if (d->wallpaper && d->wallpaper->isInitialized()) {
01664         QGraphicsItem *item = scene()->itemAt(event->scenePos());
01665         if (item == this) {
01666             event->ignore();
01667             d->wallpaper->wheelEvent(event);
01668 
01669             if (event->isAccepted()) {
01670                 return;
01671             }
01672         }
01673     }
01674 
01675     QString trigger = ContainmentActions::eventToString(event);
01676 
01677     if (d->prepareContainmentActions(trigger, event->screenPos())) {
01678         d->actionPlugins()->value(trigger)->contextEvent(event);
01679         event->accept();
01680     } else {
01681         event->ignore();
01682         Applet::wheelEvent(event);
01683     }
01684 }
01685 
01686 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
01687 {
01688     Applet *applet = qgraphicsitem_cast<Applet*>(watched);
01689 
01690     // Otherwise we're watching something we shouldn't be...
01691     Q_ASSERT(applet != 0);
01692     if (!d->applets.contains(applet)) {
01693         return false;
01694     }
01695 
01696     //kDebug() << "got sceneEvent";
01697     switch (event->type()) {
01698     case QEvent::GraphicsSceneHoverEnter:
01699         //kDebug() << "got hoverenterEvent" << immutability() << " " << applet->immutability();
01700         if (immutability() == Mutable && applet->immutability() == Mutable) {
01701             QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
01702             if (d->handles.contains(applet)) {
01703                 AppletHandle *handle = d->handles.value(applet);
01704                 if (handle) {
01705                     handle->setHoverPos(he->pos());
01706                 }
01707             } else {
01708                 //kDebug() << "generated applet handle";
01709                 AppletHandle *handle = new AppletHandle(this, applet, he->pos());
01710                 d->handles[applet] = handle;
01711                 connect(handle, SIGNAL(disappearDone(AppletHandle*)),
01712                         this, SLOT(handleDisappeared(AppletHandle*)));
01713                 connect(applet, SIGNAL(geometryChanged()),
01714                         handle, SLOT(appletResized()));
01715             }
01716         }
01717         break;
01718     case QEvent::GraphicsSceneHoverMove:
01719         if (immutability() == Mutable && applet->immutability() == Mutable) {
01720             QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
01721             if (d->handles.contains(applet)) {
01722                 AppletHandle *handle = d->handles.value(applet);
01723                 if (handle) {
01724                     handle->setHoverPos(he->pos());
01725                 }
01726             }
01727         }
01728         break;
01729     default:
01730         break;
01731     }
01732 
01733     return false;
01734 }
01735 
01736 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value)
01737 {
01738     //FIXME if the applet is moved to another containment we need to unfocus it
01739 
01740     if (isContainment() &&
01741         (change == QGraphicsItem::ItemSceneHasChanged ||
01742          change == QGraphicsItem::ItemPositionHasChanged)) {
01743         switch (d->type) {
01744             case PanelContainment:
01745             case CustomPanelContainment:
01746                 d->positionPanel();
01747                 break;
01748             default:
01749                 if (corona()) {
01750                     QMetaObject::invokeMethod(corona(), "layoutContainments");
01751                 }
01752                 break;
01753         }
01754     }
01755 
01756     return Applet::itemChange(change, value);
01757 }
01758 
01759 void Containment::enableAction(const QString &name, bool enable)
01760 {
01761     QAction *action = this->action(name);
01762     if (action) {
01763         action->setEnabled(enable);
01764         action->setVisible(enable);
01765     }
01766 }
01767 
01768 void Containment::addToolBoxAction(QAction *action)
01769 {
01770     d->createToolBox();
01771     if (d->toolBox) {
01772         d->toolBox.data()->addTool(action);
01773     }
01774 }
01775 
01776 void Containment::removeToolBoxAction(QAction *action)
01777 {
01778     if (d->toolBox) {
01779         d->toolBox.data()->removeTool(action);
01780     }
01781 }
01782 
01783 void Containment::setToolBoxOpen(bool open)
01784 {
01785     if (open) {
01786         openToolBox();
01787     } else {
01788         closeToolBox();
01789     }
01790 }
01791 
01792 bool Containment::isToolBoxOpen() const
01793 {
01794     return (d->toolBox && d->toolBox.data()->isShowing());
01795 }
01796 
01797 void Containment::openToolBox()
01798 {
01799     if (d->toolBox && !d->toolBox.data()->isShowing()) {
01800         d->toolBox.data()->setShowing(true);
01801         emit toolBoxVisibilityChanged(true);
01802     }
01803 }
01804 
01805 void Containment::closeToolBox()
01806 {
01807     if (d->toolBox && d->toolBox.data()->isShowing()) {
01808         d->toolBox.data()->setShowing(false);
01809         emit toolBoxVisibilityChanged(false);
01810     }
01811 }
01812 
01813 void Containment::addAssociatedWidget(QWidget *widget)
01814 {
01815     Applet::addAssociatedWidget(widget);
01816     if (d->focusedApplet) {
01817         d->focusedApplet->addAssociatedWidget(widget);
01818     }
01819 
01820     foreach (const Applet *applet, d->applets) {
01821         if (applet->d->activationAction) {
01822             widget->addAction(applet->d->activationAction);
01823         }
01824     }
01825 }
01826 
01827 void Containment::removeAssociatedWidget(QWidget *widget)
01828 {
01829     Applet::removeAssociatedWidget(widget);
01830     if (d->focusedApplet) {
01831         d->focusedApplet->removeAssociatedWidget(widget);
01832     }
01833 
01834     foreach (const Applet *applet, d->applets) {
01835         if (applet->d->activationAction) {
01836             widget->removeAction(applet->d->activationAction);
01837         }
01838     }
01839 }
01840 
01841 void Containment::setDrawWallpaper(bool drawWallpaper)
01842 {
01843     d->drawWallpaper = drawWallpaper;
01844     if (drawWallpaper) {
01845         KConfigGroup cfg = config();
01846         const QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper);
01847         const QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode);
01848         setWallpaper(wallpaper, mode);
01849     } else {
01850         delete d->wallpaper;
01851         d->wallpaper = 0;
01852     }
01853 }
01854 
01855 bool Containment::drawWallpaper()
01856 {
01857     return d->drawWallpaper;
01858 }
01859 
01860 void Containment::setWallpaper(const QString &pluginName, const QString &mode)
01861 {
01862     KConfigGroup cfg = config();
01863     bool newPlugin = true;
01864     bool newMode = true;
01865 
01866     if (d->drawWallpaper) {
01867         if (d->wallpaper) {
01868             // we have a wallpaper, so let's decide whether we need to swap it out
01869             if (d->wallpaper->pluginName() != pluginName) {
01870                 delete d->wallpaper;
01871                 d->wallpaper = 0;
01872             } else {
01873                 // it's the same plugin, so let's save its state now so when
01874                 // we call restore later on we're safe
01875                 newMode = d->wallpaper->renderingMode().name() != mode;
01876                 newPlugin = false;
01877             }
01878         }
01879 
01880         if (!pluginName.isEmpty() && !d->wallpaper) {
01881             d->wallpaper = Plasma::Wallpaper::load(pluginName);
01882         }
01883 
01884         if (d->wallpaper) {
01885             d->wallpaper->setParent(this);
01886             d->wallpaper->setBoundingRect(QRectF(QPointF(0, 0), size()));
01887             d->wallpaper->setRenderingMode(mode);
01888 
01889             if (newPlugin) {
01890                 cfg.writeEntry("wallpaperplugin", pluginName);
01891             }
01892 
01893             if (d->wallpaper->isInitialized()) {
01894                 KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper");
01895                 wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName);
01896                 d->wallpaper->restore(wallpaperConfig);
01897             }
01898 
01899             if (newMode) {
01900                 cfg.writeEntry("wallpaperpluginmode", mode);
01901             }
01902         }
01903 
01904         update();
01905     }
01906 
01907     if (!d->wallpaper) {
01908         cfg.deleteEntry("wallpaperplugin");
01909         cfg.deleteEntry("wallpaperpluginmode");
01910     }
01911 
01912     if (newPlugin || newMode) {
01913         if (newPlugin && d->wallpaper) {
01914             connect(d->wallpaper, SIGNAL(configureRequested()), this, SLOT(requestConfiguration()));
01915             connect(d->wallpaper, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
01916         }
01917 
01918         emit configNeedsSaving();
01919     }
01920 }
01921 
01922 Plasma::Wallpaper *Containment::wallpaper() const
01923 {
01924     return d->wallpaper;
01925 }
01926 
01927 void Containment::setContainmentActions(const QString &trigger, const QString &pluginName)
01928 {
01929     KConfigGroup cfg = containmentActionsConfig();
01930     ContainmentActions *plugin = 0;
01931 
01932     if (d->actionPlugins()->contains(trigger)) {
01933         plugin = d->actionPlugins()->value(trigger);
01934         if (plugin->pluginName() != pluginName) {
01935             d->actionPlugins()->remove(trigger);
01936             delete plugin;
01937             plugin=0;
01938         }
01939     }
01940     if (pluginName.isEmpty()) {
01941         cfg.deleteEntry(trigger);
01942     } else if (plugin) {
01943         //it already existed, just reload config
01944         if (plugin->isInitialized()) {
01945             plugin->setContainment(this); //to be safe
01946             //FIXME make a truly unique config group
01947             KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
01948             plugin->restore(pluginConfig);
01949         }
01950     } else {
01951         switch (d->containmentActionsSource) {
01952         case ContainmentPrivate::Activity:
01953             //FIXME
01954         case ContainmentPrivate::Local:
01955             plugin = ContainmentActions::load(this, pluginName);
01956             break;
01957         default:
01958             plugin = ContainmentActions::load(0, pluginName);
01959         }
01960         if (plugin) {
01961             cfg.writeEntry(trigger, pluginName);
01962             d->actionPlugins()->insert(trigger, plugin);
01963         } else {
01964             //bad plugin... gets removed. is this a feature or a bug?
01965             cfg.deleteEntry(trigger);
01966         }
01967     }
01968 
01969     emit configNeedsSaving();
01970 }
01971 
01972 QStringList Containment::containmentActionsTriggers()
01973 {
01974     return d->actionPlugins()->keys();
01975 }
01976 
01977 QString Containment::containmentActions(const QString &trigger)
01978 {
01979     ContainmentActions *c = d->actionPlugins()->value(trigger);
01980     return c ? c->pluginName() : QString();
01981 }
01982 
01983 void Containment::setActivity(const QString &activity)
01984 {
01985     Context *context = d->context();
01986     if (context->currentActivity() != activity) {
01987         context->setCurrentActivity(activity);
01988     }
01989 }
01990 
01991 void ContainmentPrivate::onContextChanged(Plasma::Context *con)
01992 {
01993     foreach (Applet *a, applets) {
01994         a->updateConstraints(ContextConstraint);
01995     }
01996 
01997     KConfigGroup c = q->config();
01998     QString act = con->currentActivityId();
01999 
02000     //save anything that's been set (boy I hope this avoids overwriting things)
02001     //FIXME of course if the user sets the name to an empty string we have a bug
02002     //but once we get context retrieving the name as soon as the id is set, this issue should go away
02003     if (!act.isEmpty()) {
02004         c.writeEntry("activityId", act);
02005     }
02006     act = con->currentActivity();
02007     if (!act.isEmpty()) {
02008         c.writeEntry("activity", act);
02009     }
02010 
02011     if (toolBox) {
02012         toolBox.data()->update();
02013     }
02014     emit q->configNeedsSaving();
02015     emit q->contextChanged(con);
02016 }
02017 
02018 QString Containment::activity() const
02019 {
02020     return d->context()->currentActivity();
02021 }
02022 
02023 Context *Containment::context() const
02024 {
02025     return d->context();
02026 }
02027 
02028 Context *ContainmentPrivate::context()
02029 {
02030     if (!con) {
02031         con = new Context(q);
02032         q->connect(con, SIGNAL(changed(Plasma::Context*)),
02033                    q, SLOT(onContextChanged(Plasma::Context*)));
02034     }
02035 
02036     return con;
02037 }
02038 
02039 KActionCollection* ContainmentPrivate::actions()
02040 {
02041     return static_cast<Applet*>(q)->d->actions;
02042 }
02043 
02044 void ContainmentPrivate::focusApplet(Plasma::Applet *applet)
02045 {
02046     if (focusedApplet == applet) {
02047         return;
02048     }
02049 
02050     QList<QWidget *> widgets = actions()->associatedWidgets();
02051     if (focusedApplet) {
02052         foreach (QWidget *w, widgets) {
02053             focusedApplet->removeAssociatedWidget(w);
02054         }
02055     }
02056 
02057     if (applet && applets.contains(applet)) {
02058         //kDebug() << "switching to" << applet->name();
02059         focusedApplet = applet;
02060         foreach (QWidget *w, widgets) {
02061             focusedApplet->addAssociatedWidget(w);
02062         }
02063 
02064         if (!focusedApplet->hasFocus()) {
02065             focusedApplet->setFocus(Qt::ShortcutFocusReason);
02066         }
02067     } else {
02068         focusedApplet = 0;
02069     }
02070 }
02071 
02072 void Containment::focusNextApplet()
02073 {
02074     if (d->applets.isEmpty()) {
02075         return;
02076     }
02077     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
02078     if (index >= d->applets.size()) {
02079         index = 0;
02080     }
02081     kDebug() << "index" << index;
02082     d->focusApplet(d->applets.at(index));
02083 }
02084 
02085 void Containment::focusPreviousApplet()
02086 {
02087     if (d->applets.isEmpty()) {
02088         return;
02089     }
02090     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
02091     if (index < 0) {
02092         index = d->applets.size() - 1;
02093     }
02094     kDebug() << "index" << index;
02095     d->focusApplet(d->applets.at(index));
02096 }
02097 
02098 void Containment::destroy()
02099 {
02100     destroy(true);
02101 }
02102 
02103 void Containment::showConfigurationInterface()
02104 {
02105     Applet::showConfigurationInterface();
02106 }
02107 
02108 void Containment::configChanged()
02109 {
02110     if (d->drawWallpaper) {
02111         KConfigGroup group = config();
02112         setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
02113                      group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
02114     }
02115 
02116     Applet::configChanged();
02117 }
02118 
02119 void ContainmentPrivate::requestConfiguration()
02120 {
02121     emit q->configureRequested(q);
02122 }
02123 
02124 void ContainmentPrivate::checkStatus(Plasma::ItemStatus appletStatus)
02125 {
02126     //kDebug() << "================== "<< appletStatus << q->status();
02127     if (appletStatus == q->status()) {
02128         emit q->newStatus(appletStatus);
02129         return;
02130     }
02131 
02132     if (appletStatus < q->status()) {
02133         // check to see if any other applet has a higher status, and stick with that
02134         // if we do
02135         foreach (Applet *applet, applets) {
02136             if (applet->status() > appletStatus) {
02137                 appletStatus = applet->status();
02138             }
02139         }
02140     }
02141 
02142     q->setStatus(appletStatus);
02143 }
02144 
02145 void Containment::destroy(bool confirm)
02146 {
02147     if (immutability() != Mutable || Applet::d->transient) {
02148         return;
02149     }
02150 
02151     if (isContainment()) {
02152         //FIXME maybe that %1 should be the containment type not the name
02153         //FIXME: should not be blocking
02154         if (!confirm ||
02155             KMessageBox::warningContinueCancel(
02156                 view(),
02157                 i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()),
02158                 i18nc("@title:window %1 is the name of the containment", "Remove %1", name()), KStandardGuiItem::remove()) == KMessageBox::Continue) {
02159             //clearApplets();
02160             Applet::destroy();
02161         }
02162     } else {
02163         Applet::destroy();
02164     }
02165 }
02166 
02167 void ContainmentPrivate::createToolBox()
02168 {
02169     if (!toolBox) {
02170         toolBox = Plasma::AbstractToolBox::load(q->corona()->preferredToolBoxPlugin(type), QVariantList(), q);
02171 
02172         if (toolBox) {
02173             QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
02174             QObject::connect(toolBox.data(), SIGNAL(toggled()), q, SLOT(updateToolBoxVisibility()));
02175 
02176             positionToolBox();
02177         }
02178     }
02179 }
02180 
02181 void ContainmentPrivate::positionToolBox()
02182 {
02183     QMetaObject::invokeMethod(toolBox.data(), "reposition");
02184 }
02185 
02186 void ContainmentPrivate::updateToolBoxVisibility()
02187 {
02188     emit q->toolBoxVisibilityChanged(toolBox.data()->isShowing());
02189 }
02190 
02191 void ContainmentPrivate::triggerShowAddWidgets()
02192 {
02193     emit q->showAddWidgetsInterface(QPointF());
02194 }
02195 
02196 void ContainmentPrivate::handleDisappeared(AppletHandle *handle)
02197 {
02198     if (handles.contains(handle->applet())) {
02199         handles.remove(handle->applet());
02200         handle->detachApplet();
02201         QGraphicsScene *scene = q->scene();
02202         if (scene && handle->scene() == scene) {
02203             scene->removeItem(handle);
02204         }
02205         handle->deleteLater();
02206     }
02207 }
02208 
02209 void ContainmentPrivate::checkRemoveAction()
02210 {
02211     q->enableAction("remove", q->immutability() == Mutable);
02212 }
02213 
02214 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
02215 {
02216     if (!q->isContainment()) {
02217         return;
02218     }
02219 
02220     //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox;
02221     if (constraints & Plasma::ImmutableConstraint) {
02222         //update actions
02223         checkRemoveAction();
02224         const bool unlocked = q->immutability() == Mutable;
02225         q->setAcceptDrops(unlocked);
02226         q->enableAction("add widgets", unlocked);
02227 
02228         // tell the applets too
02229         foreach (Applet *a, applets) {
02230             a->setImmutability(q->immutability());
02231             a->updateConstraints(ImmutableConstraint);
02232         }
02233 
02234         //clear handles on lock
02235         if (!unlocked) {
02236             QMap<Applet*, AppletHandle*> h = handles;
02237             handles.clear();
02238 
02239             foreach (AppletHandle *handle, h) {
02240                 handle->disconnect(q);
02241 
02242                 if (q->scene()) {
02243                     q->scene()->removeItem(handle);
02244                 }
02245 
02246                 handle->deleteLater();
02247             }
02248         }
02249     }
02250 
02251     // pass on the constraints that are relevant here
02252     Constraints appletConstraints = NoConstraint;
02253     if (constraints & FormFactorConstraint) {
02254         appletConstraints |= FormFactorConstraint;
02255     }
02256 
02257     if (constraints & ScreenConstraint) {
02258         appletConstraints |= ScreenConstraint;
02259     }
02260 
02261     if (appletConstraints != NoConstraint) {
02262         foreach (Applet *applet, applets) {
02263             applet->updateConstraints(appletConstraints);
02264         }
02265     }
02266 
02267     if (toolBox && (constraints & Plasma::SizeConstraint ||
02268                     constraints & Plasma::FormFactorConstraint ||
02269                     constraints & Plasma::ScreenConstraint ||
02270                     constraints & Plasma::StartupCompletedConstraint)) {
02271         //kDebug() << "Positioning toolbox";
02272         positionToolBox();
02273     }
02274 
02275     if (constraints & Plasma::StartupCompletedConstraint && type < Containment::CustomContainment) {
02276         q->addToolBoxAction(q->action("remove"));
02277         checkRemoveAction();
02278     }
02279 }
02280 
02281 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args,
02282                                       const QRectF &appletGeometry, uint id, bool delayInit)
02283 {
02284     if (!q->isContainment()) {
02285         return 0;
02286     }
02287 
02288     if (!delayInit && q->immutability() != Mutable) {
02289         kDebug() << "addApplet for" << name << "requested, but we're currently immutable!";
02290         return 0;
02291     }
02292 
02293     QGraphicsView *v = q->view();
02294     if (v) {
02295         v->setCursor(Qt::BusyCursor);
02296     }
02297 
02298     Applet *applet = Applet::load(name, id, args);
02299     if (v) {
02300         v->unsetCursor();
02301     }
02302 
02303     if (!applet) {
02304         kDebug() << "Applet" << name << "could not be loaded.";
02305         applet = new Applet(0, QString(), id);
02306         applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name));
02307     }
02308 
02309     //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry();
02310 
02311     q->addApplet(applet, appletGeometry.topLeft(), delayInit);
02312     return applet;
02313 }
02314 
02315 bool ContainmentPrivate::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) const
02316 {
02317     foreach (Applet *applet, applets) {
02318         if (applet != ignoredApplet && applet->geometry().intersects(region)) {
02319             return false;
02320         }
02321     }
02322     return true;
02323 }
02324 
02325 void ContainmentPrivate::appletDestroyed(Plasma::Applet *applet)
02326 {
02327     applets.removeAll(applet);
02328     if (focusedApplet == applet) {
02329         focusedApplet = 0;
02330     }
02331 
02332     if (handles.contains(applet)) {
02333         handles.remove(applet);
02334     }
02335 
02336     emit q->appletRemoved(applet);
02337     emit q->configNeedsSaving();
02338 }
02339 
02340 void ContainmentPrivate::appletAppearAnimationComplete()
02341 {
02342     Animation *anim = qobject_cast<Animation *>(q->sender());
02343     if (anim) {
02344         Applet *applet = qobject_cast<Applet*>(anim->targetWidget());
02345         if (applet) {
02346             appletAppeared(applet);
02347         }
02348     }
02349 }
02350 
02351 void ContainmentPrivate::appletAppeared(Applet *applet)
02352 {
02353     kDebug() << type << Containment::DesktopContainment;
02354     if (type == Containment::DesktopContainment) {
02355         applet->installSceneEventFilter(q);
02356     }
02357 
02358     KConfigGroup *cg = applet->d->mainConfigGroup();
02359     applet->save(*cg);
02360     emit q->configNeedsSaving();
02361 }
02362 
02363 void ContainmentPrivate::positionPanel(bool force)
02364 {
02365     if (!q->scene()) {
02366         kDebug() << "no scene yet";
02367         return;
02368     }
02369 
02370     // already positioning the panel - avoid infinite loops
02371     if (ContainmentPrivate::s_positioningPanels) {
02372         return;
02373     }
02374 
02375     // we position panels in negative coordinates, and stack all horizontal
02376     // and all vertical panels with each other.
02377 
02378 
02379     const QPointF p = q->pos();
02380 
02381     if (!force &&
02382         p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
02383         q->scene()->collidingItems(q).isEmpty()) {
02384         // already positioned and not running into any other panels
02385         return;
02386     }
02387 
02388 
02389     QPointF newPos = preferredPanelPos(q->corona());
02390     if (p != newPos) {
02391         ContainmentPrivate::s_positioningPanels = true;
02392         q->setPos(newPos);
02393         ContainmentPrivate::s_positioningPanels = false;
02394     }
02395 }
02396 
02397 bool ContainmentPrivate::isPanelContainment() const
02398 {
02399     return type == Containment::PanelContainment || type == Containment::CustomPanelContainment;
02400 }
02401 
02402 QPointF ContainmentPrivate::preferredPos(Corona *corona) const
02403 {
02404     Q_ASSERT(corona);
02405 
02406     if (isPanelContainment()) {
02407         //kDebug() << "is a panel, so put it at" << preferredPanelPos(corona);
02408         return preferredPanelPos(corona);
02409     }
02410 
02411     QPointF pos(0, 0);
02412     QTransform t;
02413     while (QGraphicsItem *i = corona->itemAt(pos, t)) {
02414         pos.setX(i->scenePos().x() + i->boundingRect().width() + 10);
02415     }
02416 
02417     //kDebug() << "not a panel, put it at" << pos;
02418     return pos;
02419 }
02420 
02421 QPointF ContainmentPrivate::preferredPanelPos(Corona *corona) const
02422 {
02423     Q_ASSERT(corona);
02424 
02425     //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here
02426     bool horiz = formFactor == Plasma::Horizontal;
02427     qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
02428     qreal lastHeight = 0;
02429 
02430     // this should be ok for small numbers of panels, but if we ever end
02431     // up managing hundreds of them, this simplistic alogrithm will
02432     // likely be too slow.
02433     foreach (const Containment *other, corona->containments()) {
02434         if (other == q ||
02435             !other->d->isPanelContainment() ||
02436             horiz != (other->formFactor() == Plasma::Horizontal)) {
02437             // only line up with panels of the same orientation
02438             continue;
02439         }
02440 
02441         if (horiz) {
02442             qreal y = other->pos().y();
02443             if (y < bottom) {
02444                 lastHeight = other->size().height();
02445                 bottom = y;
02446             }
02447         } else {
02448             qreal width = other->size().width();
02449             qreal x = other->pos().x() + width;
02450             if (x > bottom) {
02451                 lastHeight = width;
02452                 bottom = x + lastHeight;
02453             }
02454         }
02455     }
02456 
02457     // give a space equal to the height again of the last item so there is
02458     // room to grow.
02459     QPointF newPos;
02460     if (horiz) {
02461         bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
02462         //TODO: fix x position for non-flush-left panels
02463         kDebug() << "moved to" << QPointF(0, bottom - q->size().height());
02464         newPos = QPointF(0, bottom - q->size().height());
02465     } else {
02466         bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
02467         //TODO: fix y position for non-flush-top panels
02468         kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
02469         newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
02470     }
02471 
02472     return newPos;
02473 }
02474 
02475 
02476 bool ContainmentPrivate::prepareContainmentActions(const QString &trigger, const QPoint &screenPos, KMenu *menu)
02477 {
02478     ContainmentActions *plugin = actionPlugins()->value(trigger);
02479     if (!plugin) {
02480         return false;
02481     }
02482     plugin->setContainment(q);
02483 
02484     if (!plugin->isInitialized()) {
02485         KConfigGroup cfg = q->containmentActionsConfig();
02486         KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
02487         plugin->restore(pluginConfig);
02488     }
02489 
02490     if (plugin->configurationRequired()) {
02491         KMenu *localMenu = menu ? menu : new KMenu();
02492 
02493         localMenu->addTitle(i18n("This plugin needs to be configured"));
02494         localMenu->addAction(q->action("configure"));
02495 
02496         if (!menu) {
02497             localMenu->exec(screenPos);
02498             delete localMenu;
02499         }
02500 
02501         return false;
02502     } else if (menu) {
02503         QList<QAction*> actions = plugin->contextualActions();
02504         if (actions.isEmpty()) {
02505             //it probably didn't bother implementing the function. give the user a chance to set
02506             //a better plugin.  note that if the user sets no-plugin this won't happen...
02507             if (!isPanelContainment() && q->action("configure")) {
02508                 menu->addAction(q->action("configure"));
02509             }
02510         } else {
02511             menu->addActions(actions);
02512         }
02513     }
02514 
02515     return true;
02516 }
02517 
02518 KConfigGroup Containment::containmentActionsConfig()
02519 {
02520     KConfigGroup cfg;
02521     switch (d->containmentActionsSource) {
02522     case ContainmentPrivate::Local:
02523         cfg = config();
02524         cfg = KConfigGroup(&cfg, "ActionPlugins");
02525         break;
02526     case ContainmentPrivate::Activity:
02527         cfg = KConfigGroup(corona()->config(), "Activities");
02528         cfg = KConfigGroup(&cfg, d->context()->currentActivityId());
02529         cfg = KConfigGroup(&cfg, "ActionPlugins");
02530         break;
02531     default:
02532         cfg = KConfigGroup(corona()->config(), "ActionPlugins");
02533     }
02534     return cfg;
02535 }
02536 
02537 QHash<QString, ContainmentActions*> * ContainmentPrivate::actionPlugins()
02538 {
02539     switch (containmentActionsSource) {
02540         case Activity:
02541             //FIXME
02542         case Local:
02543             return &localActionPlugins;
02544         default:
02545             return &globalActionPlugins;
02546     }
02547 }
02548 
02549 } // Plasma namespace
02550 
02551 #include "containment.moc"
02552 

Plasma

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

kdelibs

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