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

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
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