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

Plasma

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

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs-4.9.5 API Reference

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

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