• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU Library General Public License as
00007  *   published by the Free Software Foundation; either version 2, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details
00014  *
00015  *   You should have received a copy of the GNU Library General Public
00016  *   License along with this program; if not, write to the
00017  *   Free Software Foundation, Inc.,
00018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  */
00020 
00021 #include "containment.h"
00022 #include "private/containment_p.h"
00023 
00024 #include <QAction>
00025 #include <QFile>
00026 #include <QGraphicsSceneContextMenuEvent>
00027 #include <QGraphicsView>
00028 #include <QMimeData>
00029 #include <QPainter>
00030 #include <QStyleOptionGraphicsItem>
00031 #include <QGraphicsLayout>
00032 #include <QGraphicsLinearLayout>
00033 
00034 #include <kaction.h>
00035 #include <kapplication.h>
00036 #include <kauthorized.h>
00037 #include <kicon.h>
00038 #include <kmenu.h>
00039 #include <kmessagebox.h>
00040 #include <kmimetype.h>
00041 #include <krun.h>
00042 #include <kservicetypetrader.h>
00043 #include <kstandarddirs.h>
00044 #include <ktemporaryfile.h>
00045 #include <kwindowsystem.h>
00046 
00047 #include "animator.h"
00048 #include "context.h"
00049 #include "corona.h"
00050 #include "svg.h"
00051 #include "wallpaper.h"
00052 
00053 #include "private/applet_p.h"
00054 #include "private/applethandle_p.h"
00055 #include "private/desktoptoolbox_p.h"
00056 #include "private/paneltoolbox_p.h"
00057 
00058 namespace Plasma
00059 {
00060 
00061 bool ContainmentPrivate::s_positioning = false;
00062 static const char defaultWallpaper[] = "image";
00063 static const char defaultWallpaperMode[] = "SingleImage";
00064 
00065 Containment::StyleOption::StyleOption()
00066     : QStyleOptionGraphicsItem(),
00067       view(0)
00068 {
00069     version = Version;
00070     type = Type;
00071 }
00072 
00073 Containment::StyleOption::StyleOption(const Containment::StyleOption & other)
00074     : QStyleOptionGraphicsItem(other),
00075       view(other.view)
00076 {
00077     version = Version;
00078     type = Type;
00079 }
00080 
00081 Containment::StyleOption::StyleOption(const QStyleOptionGraphicsItem &other)
00082     : QStyleOptionGraphicsItem(other),
00083       view(0)
00084 {
00085     version = Version;
00086     type = Type;
00087 }
00088 
00089 Containment::Containment(QGraphicsItem *parent,
00090                          const QString &serviceId,
00091                          uint containmentId)
00092     : Applet(parent, serviceId, containmentId),
00093       d(new ContainmentPrivate(this))
00094 {
00095     // WARNING: do not access config() OR globalConfig() in this method!
00096     //          that requires a scene, which is not available at this point
00097     setPos(0, 0);
00098     setBackgroundHints(NoBackground);
00099     setContainmentType(CustomContainment);
00100 }
00101 
00102 Containment::Containment(QObject *parent, const QVariantList &args)
00103     : Applet(parent, args),
00104       d(new ContainmentPrivate(this))
00105 {
00106     // WARNING: do not access config() OR globalConfig() in this method!
00107     //          that requires a scene, which is not available at this point
00108     setPos(0, 0);
00109     setBackgroundHints(NoBackground);
00110 }
00111 
00112 Containment::~Containment()
00113 {
00114     if (Applet::d->transient) {
00115         Applet::d->resetConfigurationObject();
00116         Applet::d->transient = false;
00117     }
00118 
00119     delete d;
00120 }
00121 
00122 void Containment::init()
00123 {
00124     if (!isContainment()) {
00125         return;
00126     }
00127 
00128     setCacheMode(NoCache);
00129     setFlag(QGraphicsItem::ItemIsMovable, false);
00130     setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
00131     setAcceptDrops(true);
00132     setAcceptsHoverEvents(true);
00133 
00134     //TODO: would be nice to not do this on init, as it causes Animator to init
00135     connect(Animator::self(), SIGNAL(animationFinished(QGraphicsItem*,Plasma::Animator::Animation)),
00136             this, SLOT(containmentAppletAnimationComplete(QGraphicsItem*,Plasma::Animator::Animation)));
00137 
00138     if (d->type == NoContainmentType) {
00139         setContainmentType(DesktopContainment);
00140     }
00141 
00142     //common actions
00143     bool unlocked = immutability() == Mutable;
00144 
00145     QAction *appletBrowserAction = new QAction(i18n("Add Widgets..."), this);
00146     appletBrowserAction->setIcon(KIcon("list-add"));
00147     appletBrowserAction->setVisible(unlocked);
00148     appletBrowserAction->setEnabled(unlocked);
00149     connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets()));
00150     appletBrowserAction->setShortcutContext(Qt::WidgetShortcut);
00151     //appletBrowserAction->setShortcut(QKeySequence("ctrl+a")); //FIXME in KDE 4.3, see bug #165938
00152     d->actions().addAction("add widgets", appletBrowserAction);
00153 
00154     QAction *configureActivityAction = new QAction(i18n("Appearance Settings"), this);
00155     configureActivityAction->setIcon(KIcon("configure"));
00156     bool canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked");
00157     configureActivityAction->setVisible(canConfig);
00158     configureActivityAction->setEnabled(canConfig);
00159     connect(configureActivityAction, SIGNAL(triggered()), this, SLOT(requestConfiguration()));
00160     d->actions().addAction("activity settings", configureActivityAction);
00161 
00162     QAction *action = new QAction(i18n("Next Widget"), this);
00163     //no icon
00164     connect(action, SIGNAL(triggered()), this, SLOT(focusNextApplet()));
00165     action->setShortcutContext(Qt::WidgetShortcut);
00166     action->setShortcut(QKeySequence("ctrl+n"));
00167     d->actions().addAction("next applet", action);
00168 
00169     action = new QAction(i18n("Previous Widget"), this);
00170     //no icon
00171     connect(action, SIGNAL(triggered()), this, SLOT(focusPreviousApplet()));
00172     action->setShortcutContext(Qt::WidgetShortcut);
00173     action->setShortcut(QKeySequence("ctrl+p"));
00174     d->actions().addAction("previous applet", action);
00175 
00176     if (immutability() != SystemImmutable) {
00177         //FIXME I'm not certain this belongs in Containment
00178         //but it sure is nice to have the keyboard shortcut in every containment by default
00179         QAction *lockDesktopAction =
00180             new QAction(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets"), this);
00181         lockDesktopAction->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked"));
00182         connect(lockDesktopAction, SIGNAL(triggered(bool)),
00183                 this, SLOT(toggleDesktopImmutability()));
00184         lockDesktopAction->setShortcutContext(Qt::WidgetShortcut);
00185         lockDesktopAction->setShortcut(QKeySequence("ctrl+l"));
00186         d->actions().addAction("lock widgets", lockDesktopAction);
00187     }
00188 
00189     if (d->type != PanelContainment &&
00190         d->type != CustomPanelContainment) {
00191         QAction *zoomAction = new QAction(i18n("Zoom In"), this);
00192         zoomAction->setIcon(KIcon("zoom-in"));
00193         connect(zoomAction, SIGNAL(triggered(bool)), this, SLOT(zoomIn()));
00194         zoomAction->setShortcutContext(Qt::WidgetShortcut);
00195         //two shortcuts because I hate ctrl-+ but others expect it
00196         QList<QKeySequence> keys;
00197         keys << QKeySequence(QKeySequence::ZoomIn);
00198         keys << QKeySequence("ctrl+=");
00199         zoomAction->setShortcuts(keys);
00200         d->actions().addAction("zoom in", zoomAction);
00201 
00202         zoomAction = new QAction(i18n("Zoom Out"), this);
00203         zoomAction->setIcon(KIcon("zoom-out"));
00204         connect(zoomAction, SIGNAL(triggered(bool)), this, SLOT(zoomOut()));
00205         zoomAction->setShortcutContext(Qt::WidgetShortcut);
00206         zoomAction->setShortcut(QKeySequence(QKeySequence::ZoomOut));
00207         d->actions().addAction("zoom out", zoomAction);
00208 
00209         QAction *activityAction = new QAction(i18n("Add Activity"), this);
00210         activityAction->setIcon(KIcon("list-add"));
00211         activityAction->setVisible(unlocked);
00212         activityAction->setEnabled(unlocked);
00213         connect(activityAction, SIGNAL(triggered(bool)), this, SLOT(addSiblingContainment()));
00214         activityAction->setShortcutContext(Qt::WidgetShortcut);
00215         activityAction->setShortcut(QKeySequence("ctrl+shift+a"));
00216         d->actions().addAction("add sibling containment", activityAction);
00217 
00218         if (d->type == DesktopContainment && d->toolBox) {
00219             d->toolBox->addTool(this->action("add widgets"));
00220             d->toolBox->addTool(this->action("add sibling containment"));
00221             d->toolBox->addTool(this->action("zoom in"));
00222             d->toolBox->addTool(this->action("zoom out"));
00223 
00224             if (immutability() != SystemImmutable) {
00225                 d->toolBox->addTool(this->action("lock widgets"));
00226             }
00227             d->toolBox->addTool(this->action("activity settings"));
00228             if (hasConfigurationInterface()) {
00229                 // re-use the contianment's action.
00230                 QAction *configureContainment = this->action("configure");
00231                 if (configureContainment) {
00232                     d->toolBox->addTool(this->action("configure"));
00233                 }
00234             }
00235         }
00236 
00237         //Set a default wallpaper the first time the containment is created,
00238         //for instance from the toolbox by the user
00239         if (d->drawWallpaper) {
00240             setDrawWallpaper(true);
00241         }
00242     }
00243 }
00244 
00245 // helper function for sorting the list of applets
00246 bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2)
00247 {
00248     QPointF p1 = c1.readEntry("geometry", QRectF()).topLeft();
00249     QPointF p2 = c2.readEntry("geometry", QRectF()).topLeft();
00250 
00251     if (!qFuzzyCompare(p1.x(), p2.x())) {
00252         return p1.x() < p2.x();
00253     }
00254 
00255     return qFuzzyCompare(p1.y(), p2.y()) || p1.y() < p2.y();
00256 }
00257 
00258 void Containment::restore(KConfigGroup &group)
00259 {
00260     /*kDebug() << "!!!!!!!!!!!!initConstraints" << group.name() << containmentType();
00261     kDebug() << "    location:" << group.readEntry("location", (int)d->location);
00262     kDebug() << "    geom:" << group.readEntry("geometry", geometry());
00263     kDebug() << "    formfactor:" << group.readEntry("formfactor", (int)d->formFactor);
00264     kDebug() << "    screen:" << group.readEntry("screen", d->screen);*/
00265     if (!isContainment()) {
00266         Applet::restore(group);
00267         return;
00268     }
00269 
00270     QRectF geo = group.readEntry("geometry", geometry());
00271     //override max/min
00272     //this ensures panels are set to their saved size even when they have max & min set to prevent
00273     //resizing
00274     if (geo.size() != geo.size().boundedTo(maximumSize())) {
00275         setMaximumSize(maximumSize().expandedTo(geo.size()));
00276     }
00277     if (geo.size() != geo.size().expandedTo(minimumSize())) {
00278         setMinimumSize(minimumSize().boundedTo(geo.size()));
00279     }
00280     setGeometry(geo);
00281 
00282     setLocation((Plasma::Location)group.readEntry("location", (int)d->location));
00283     setFormFactor((Plasma::FormFactor)group.readEntry("formfactor", (int)d->formFactor));
00284     setScreen(group.readEntry("screen", d->screen), group.readEntry("desktop", d->desktop));
00285     setActivity(group.readEntry("activity", QString()));
00286 
00287     flushPendingConstraintsEvents();
00288     restoreContents(group);
00289     setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
00290 
00291     setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper),
00292                  group.readEntry("wallpaperpluginmode", defaultWallpaperMode));
00293     /*
00294     kDebug() << "Containment" << id() <<
00295                 "screen" << screen() <<
00296                 "geometry is" << geometry() <<
00297                 "wallpaper" << ((d->wallpaper) ? d->wallpaper->pluginName() : QString()) <<
00298                 "wallpaper mode" << wallpaperMode() <<
00299                 "config entries" << group.entryMap();
00300     */
00301 }
00302 
00303 void Containment::save(KConfigGroup &g) const
00304 {
00305     KConfigGroup group = g;
00306     if (!group.isValid()) {
00307         group = config();
00308     }
00309 
00310     // locking is saved in Applet::save
00311     Applet::save(group);
00312     group.writeEntry("screen", d->screen);
00313     group.writeEntry("desktop", d->desktop);
00314     group.writeEntry("formfactor", (int)d->formFactor);
00315     group.writeEntry("location", (int)d->location);
00316     group.writeEntry("activity", d->context()->currentActivity());
00317 
00318     if (d->toolBox) {
00319         d->toolBox->save(group);
00320     }
00321 
00322     if (d->wallpaper) {
00323         group.writeEntry("wallpaperplugin", d->wallpaper->pluginName());
00324         group.writeEntry("wallpaperpluginmode", d->wallpaper->renderingMode().name());
00325 
00326         if (d->wallpaper->isInitialized()) {
00327             KConfigGroup wallpaperConfig(&group, "Wallpaper");
00328             wallpaperConfig = KConfigGroup(&wallpaperConfig, d->wallpaper->pluginName());
00329             d->wallpaper->save(wallpaperConfig);
00330         }
00331     }
00332 
00333     saveContents(group);
00334 }
00335 
00336 void Containment::saveContents(KConfigGroup &group) const
00337 {
00338     KConfigGroup applets(&group, "Applets");
00339     foreach (const Applet *applet, d->applets) {
00340         KConfigGroup appletConfig(&applets, QString::number(applet->id()));
00341         applet->save(appletConfig);
00342     }
00343 }
00344 
00345 void Containment::restoreContents(KConfigGroup &group)
00346 {
00347     KConfigGroup applets(&group, "Applets");
00348 
00349     // Sort the applet configs in order of geometry to ensure that applets
00350     // are added from left to right or top to bottom for a panel containment
00351     QList<KConfigGroup> appletConfigs;
00352     foreach (const QString &appletGroup, applets.groupList()) {
00353         //kDebug() << "reading from applet group" << appletGroup;
00354         KConfigGroup appletConfig(&applets, appletGroup);
00355         appletConfigs.append(appletConfig);
00356     }
00357     qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan);
00358 
00359     QMutableListIterator<KConfigGroup> it(appletConfigs);
00360     while (it.hasNext()) {
00361         KConfigGroup &appletConfig = it.next();
00362         int appId = appletConfig.name().toUInt();
00363         QString plugin = appletConfig.readEntry("plugin", QString());
00364 
00365         if (plugin.isEmpty()) {
00366             continue;
00367         }
00368 
00369         Applet *applet = d->addApplet(plugin, QVariantList(),
00370                                       appletConfig.readEntry("geometry", QRectF()),
00371                                       appId, true);
00372         applet->restore(appletConfig);
00373     }
00374 }
00375 
00376 Containment::Type Containment::containmentType() const
00377 {
00378     return d->type;
00379 }
00380 
00381 void Containment::setContainmentType(Containment::Type type)
00382 {
00383     if (d->type == type) {
00384         return;
00385     }
00386 
00387     delete d->toolBox;
00388     d->toolBox = 0;
00389     d->type = type;
00390 
00391     if (!isContainment()) {
00392         return;
00393     }
00394 
00395     if ((type == DesktopContainment || type == PanelContainment)) {
00396         d->createToolBox();
00397     }
00398 
00399     d->checkRemoveAction();
00400 }
00401 
00402 Corona *Containment::corona() const
00403 {
00404     return dynamic_cast<Corona*>(scene());
00405 }
00406 
00407 void Containment::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00408 {
00409     event->ignore();
00410     if (d->wallpaper && d->wallpaper->isInitialized()) {
00411         QGraphicsItem *item = scene()->itemAt(event->scenePos());
00412         if (item == this) {
00413             d->wallpaper->mouseMoveEvent(event);
00414         }
00415     }
00416 
00417     if (!event->isAccepted()) {
00418         event->accept();
00419         Applet::mouseMoveEvent(event);
00420     }
00421 }
00422 
00423 void Containment::mousePressEvent(QGraphicsSceneMouseEvent *event)
00424 {
00425     event->ignore();
00426     if (d->wallpaper && d->wallpaper->isInitialized()) {
00427         QGraphicsItem *item = scene()->itemAt(event->scenePos());
00428         if (item == this) {
00429             d->wallpaper->mousePressEvent(event);
00430         }
00431     }
00432 
00433     if (event->isAccepted()) {
00434         setFocus(Qt::MouseFocusReason);
00435     } else {
00436         event->accept();
00437         Applet::mousePressEvent(event);
00438     }
00439 }
00440 
00441 void Containment::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00442 {
00443     event->ignore();
00444     if (d->wallpaper && d->wallpaper->isInitialized()) {
00445         QGraphicsItem *item = scene()->itemAt(event->scenePos());
00446         if (item == this) {
00447             d->wallpaper->mouseReleaseEvent(event);
00448         }
00449     }
00450 
00451     if (!event->isAccepted()) {
00452         event->accept();
00453         Applet::mouseReleaseEvent(event);
00454     }
00455 }
00456 
00457 void Containment::showDropZone(const QPoint pos)
00458 {
00459     Q_UNUSED(pos)
00460     //Base implementation does nothing, don't put code here
00461 }
00462 
00463 void Containment::showContextMenu(const QPointF &containmentPos, const QPoint &screenPos)
00464 {
00465     d->showContextMenu(mapToScene(containmentPos), screenPos, false);
00466 }
00467 
00468 void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
00469 {
00470     //kDebug() << "let's see if we manage to get a context menu here, huh";
00471     if (!isContainment() || !scene() || !KAuthorized::authorizeKAction("desktop_contextmenu")) {
00472         Applet::contextMenuEvent(event);
00473         return;
00474     }
00475 
00476     if (!d->showContextMenu(event->scenePos(), event->screenPos(), true)) {
00477         Applet::contextMenuEvent(event);
00478     } else {
00479         event->accept();
00480     }
00481 }
00482 
00483 void ContainmentPrivate::containmentActions(KMenu &desktopMenu)
00484 {
00485     if (static_cast<Corona*>(q->scene())->immutability() != Mutable &&
00486         !KAuthorized::authorizeKAction("unlock_desktop")) {
00487         //kDebug() << "immutability";
00488         return;
00489     }
00490 
00491     //get base context actions
00492     QList<QAction*> actions = q->contextualActions();
00493 
00494     //find the separator to insert the activity settings before it
00495     QAction *separatorAction = 0;
00496 
00497     foreach (QAction *action, actions) {
00498         if (action) {
00499             desktopMenu.addAction(action);
00500             if (action->isSeparator()) {
00501                 separatorAction = action;
00502             }
00503         }
00504     }
00505 
00506     desktopMenu.addSeparator();
00507 
00508     //TODO: should a submenu be created if there are too many containment specific
00509     //      actions? see folderview containment
00510     if (q->containmentType() == Containment::DesktopContainment) {
00511         desktopMenu.insertAction(separatorAction, q->action("activity settings"));
00512         if (q->hasConfigurationInterface()) {
00513             desktopMenu.addAction(q->action("configure"));
00514         }
00515     }
00516 }
00517 
00518 void ContainmentPrivate::appletActions(KMenu &desktopMenu, Applet *applet, bool includeApplet)
00519 {
00520     QList<QAction*> actions;
00521 
00522     if (includeApplet) {
00523         actions = applet->contextualActions();
00524         if (!actions.isEmpty()) {
00525             foreach (QAction *action, actions) {
00526                 if (action) {
00527                     desktopMenu.addAction(action);
00528                 }
00529             }
00530         }
00531     }
00532 
00533     if (applet->hasConfigurationInterface()) {
00534         QAction *configureApplet = applet->d->actions.action("configure");
00535         if (configureApplet) {
00536             desktopMenu.addAction(configureApplet);
00537         }
00538     }
00539 
00540     KMenu *containmentMenu = new KMenu(i18nc("%1 is the name of the containment", "%1 Options", q->name()), &desktopMenu);
00541     containmentActions(*containmentMenu);
00542     if (!containmentMenu->isEmpty()) {
00543         int enabled = 0;
00544         //count number of real actions
00545         foreach(QAction *action, containmentMenu->actions()) {
00546             if(action->isEnabled() && !action->isSeparator()) {
00547                 enabled++;
00548             }
00549         }
00550 
00551         if (enabled > 0) {
00552             desktopMenu.addSeparator();
00553         }
00554 
00555         //if there is only one, don't create a submenu
00556         if(enabled < 2) {
00557             foreach(QAction *action, containmentMenu->actions()) {
00558                 desktopMenu.addAction(action); 
00559             }
00560         } else {
00561             desktopMenu.addMenu(containmentMenu);
00562         }
00563     }
00564 
00565     if (static_cast<Corona*>(q->scene())->immutability() == Mutable) {
00566         if (!desktopMenu.isEmpty()) {
00567             desktopMenu.addSeparator();
00568         }
00569 
00570         QAction *closeApplet = applet->d->actions.action("remove");
00571         if (!closeApplet) { //unlikely but not impossible
00572             closeApplet = new QAction(i18nc("%1 is the name of the applet", "Remove this %1", applet->name()), &desktopMenu);
00573             closeApplet->setIcon(KIcon("edit-delete"));
00574             QObject::connect(closeApplet, SIGNAL(triggered(bool)), applet, SLOT(destroy()));
00575         }
00576         desktopMenu.addAction(closeApplet);
00577     }
00578 }
00579 
00580 bool ContainmentPrivate::showContextMenu(const QPointF &point,
00581                                          const QPoint &screenPos, bool includeApplet)
00582 {
00583     Applet *applet = 0;
00584 
00585     QGraphicsItem *item = q->scene()->itemAt(point);
00586     if (item == q) {
00587         item = 0;
00588     }
00589 
00590     while (item) {
00591         applet = qgraphicsitem_cast<Applet*>(item);
00592         if (applet && !applet->isContainment()) {
00593             break;
00594         }
00595 
00596         // applet may have a value due to finding a containment!
00597         applet = 0;
00598         item = item->parentItem();
00599     }
00600 
00601     KMenu desktopMenu;
00602     //kDebug() << "context menu event " << (QObject*)applet;
00603     if (applet) {
00604         appletActions(desktopMenu, applet, includeApplet);
00605     } else {
00606         containmentActions(desktopMenu);
00607     }
00608 
00609     if (!desktopMenu.isEmpty()) {
00610         //kDebug() << "executing at" << screenPos;
00611         desktopMenu.exec(screenPos);
00612         return true;
00613     }
00614 
00615     return false;
00616 }
00617 
00618 void Containment::setFormFactor(FormFactor formFactor)
00619 {
00620     if (d->formFactor == formFactor) {
00621         return;
00622     }
00623 
00624     //kDebug() << "switching FF to " << formFactor;
00625     FormFactor was = d->formFactor;
00626     d->formFactor = formFactor;
00627 
00628     if (isContainment() &&
00629         was != formFactor &&
00630         (d->type == PanelContainment ||
00631          d->type == CustomPanelContainment)) {
00632         // we are a panel and we have chaged our orientation
00633         d->positionPanel(true);
00634     }
00635 
00636     updateConstraints(Plasma::FormFactorConstraint);
00637 
00638     KConfigGroup c = config();
00639     c.writeEntry("formfactor", (int)formFactor);
00640     emit configNeedsSaving();
00641 }
00642 
00643 void Containment::setLocation(Location location)
00644 {
00645     if (d->location == location) {
00646         return;
00647     }
00648 
00649     bool emitGeomChange = false;
00650 
00651     if ((location == TopEdge || location == BottomEdge) &&
00652         (d->location == TopEdge || d->location == BottomEdge)) {
00653         emitGeomChange = true;
00654     }
00655 
00656     if ((location == RightEdge || location == LeftEdge) &&
00657         (d->location == RightEdge || d->location == LeftEdge)) {
00658         emitGeomChange = true;
00659     }
00660 
00661     d->location = location;
00662 
00663     foreach (Applet *applet, d->applets) {
00664         applet->updateConstraints(Plasma::LocationConstraint);
00665     }
00666 
00667     if (emitGeomChange) {
00668         // our geometry on the scene will not actually change,
00669         // but for the purposes of views it has
00670         emit geometryChanged();
00671     }
00672 
00673     updateConstraints(Plasma::LocationConstraint);
00674 
00675     KConfigGroup c = config();
00676     c.writeEntry("location", (int)location);
00677     emit configNeedsSaving();
00678 }
00679 
00680 void Containment::addSiblingContainment()
00681 {
00682     emit addSiblingContainment(this);
00683 }
00684 
00685 void Containment::clearApplets()
00686 {
00687     foreach (Applet *applet, d->applets) {
00688         applet->d->cleanUpAndDelete();
00689     }
00690 
00691     d->applets.clear();
00692 }
00693 
00694 Applet *Containment::addApplet(const QString &name, const QVariantList &args,
00695                                const QRectF &appletGeometry)
00696 {
00697     return d->addApplet(name, args, appletGeometry);
00698 }
00699 
00700 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit)
00701 {
00702     if (!isContainment() || (!delayInit && immutability() != Mutable)) {
00703         return;
00704     }
00705 
00706     if (!applet) {
00707         kDebug() << "adding null applet!?!";
00708         return;
00709     }
00710 
00711     if (d->applets.contains(applet)) {
00712         kDebug() << "already have this applet!";
00713     }
00714 
00715     Containment *currentContainment = applet->containment();
00716 
00717     if (containmentType() == PanelContainment) {
00718         //panels don't want backgrounds, which is important when setting geometry
00719         setBackgroundHints(NoBackground);
00720     }
00721 
00722     if (currentContainment && currentContainment != this) {
00723         emit currentContainment->appletRemoved(applet);
00724         disconnect(applet, 0, currentContainment, 0);
00725         applet->removeSceneEventFilter(currentContainment);
00726         KConfigGroup oldConfig = applet->config();
00727         currentContainment->d->applets.removeAll(applet);
00728         if (currentContainment->d->handles.contains(applet)) {
00729             currentContainment->d->handles.remove(applet);
00730         }
00731         applet->setParentItem(this);
00732 
00733         // now move the old config to the new location
00734         KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
00735         oldConfig.reparent(&c);
00736         applet->d->resetConfigurationObject();
00737     } else {
00738         applet->setParentItem(this);
00739     }
00740 
00741     d->applets << applet;
00742 
00743     connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
00744     connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus()));
00745     connect(applet, SIGNAL(destroyed(QObject*)), this, SLOT(appletDestroyed(QObject*)));
00746 
00747     if (pos != QPointF(-1, -1)) {
00748         applet->setPos(pos);
00749     }
00750 
00751     if (delayInit || currentContainment) {
00752         if (containmentType() == DesktopContainment) {
00753             applet->installSceneEventFilter(this);
00754             //applet->setWindowFlags(Qt::Window);
00755         }
00756     } else {
00757         applet->init();
00758         Animator::self()->animateItem(applet, Animator::AppearAnimation);
00759     }
00760 
00761     applet->updateConstraints(Plasma::AllConstraints);
00762 
00763     if (!delayInit) {
00764         applet->flushPendingConstraintsEvents();
00765     }
00766 
00767     emit appletAdded(applet, pos);
00768 
00769     if (!currentContainment) {
00770         applet->updateConstraints(Plasma::StartupCompletedConstraint);
00771         if (!delayInit) {
00772             applet->flushPendingConstraintsEvents();
00773         }
00774     }
00775 
00776     if (!delayInit) {
00777         applet->d->scheduleModificationNotification();
00778     }
00779 }
00780 
00781 Applet::List Containment::applets() const
00782 {
00783     return d->applets;
00784 }
00785 
00786 void Containment::setScreen(int screen, int desktop)
00787 {
00788     // What we want to do in here is:
00789     //   * claim the screen as our own
00790     //   * signal whatever may be watching this containment about the switch
00791     //   * if we are a full screen containment, then:
00792     //      * resize to match the screen if we're that kind of containment
00793     //      * kick other full-screen containments off this screen
00794     //          * if we had a screen, then give our screen to the containment
00795     //            we kick out
00796     //
00797     // a screen of -1 means no associated screen.
00798     Containment *swapScreensWith(0);
00799     if (d->type == DesktopContainment || d->type >= CustomContainment) {
00800         // we want to listen to changes in work area if our screen changes
00801         if (d->screen < 0 && screen > -1) {
00802             connect(KWindowSystem::self(), SIGNAL(workAreaChanged()),
00803                     this, SLOT(positionToolBox()));
00804         } else if (screen < 0) {
00805             disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()),
00806                        this, SLOT(positionToolBox()));
00807         }
00808 
00809         if (screen > -1 && corona()) {
00810             // sanity check to make sure someone else doesn't have this screen already!
00811             Containment *currently = corona()->containmentForScreen(screen, desktop);
00812             if (currently && currently != this) {
00813                 kDebug() << "currently is on screen" << currently->screen()
00814                          << "and is" << currently->name()
00815                          << (QObject*)currently << (QObject*)this;
00816                 currently->setScreen(-1);
00817                 swapScreensWith = currently;
00818             }
00819         }
00820     }
00821 
00822     kDebug() << "setting screen to" << screen << "and we are a" << containmentType();
00823     Q_ASSERT(corona());
00824     int numScreens = corona()->numScreens();
00825     if (screen < -1) {
00826         screen = -1;
00827     }
00828 
00829 
00830     kDebug() << "setting screen to " << screen << "and type is" << containmentType();
00831     if (screen < numScreens && screen > -1) {
00832         if (containmentType() == DesktopContainment ||
00833             containmentType() >= CustomContainment) {
00834             resize(corona()->screenGeometry(screen).size());
00835         }
00836     }
00837 
00838     // -1 == All desktops
00839     if (desktop < -1 || desktop > KWindowSystem::numberOfDesktops() - 1) {
00840         desktop = -1;
00841     }
00842 
00843     d->desktop = desktop;
00844 
00845     int oldScreen = d->screen;
00846     d->screen = screen;
00847     updateConstraints(Plasma::ScreenConstraint);
00848     if (oldScreen != screen) {
00849         emit screenChanged(oldScreen, screen, this);
00850 
00851         KConfigGroup c = config();
00852         c.writeEntry("screen", d->screen);
00853         emit configNeedsSaving();
00854     }
00855 
00856     if (swapScreensWith) {
00857         swapScreensWith->setScreen(oldScreen);
00858     }
00859 
00860     d->checkRemoveAction();
00861 }
00862 
00863 int Containment::screen() const
00864 {
00865     return d->screen;
00866 }
00867 
00868 int Containment::desktop() const
00869 {
00870     return d->desktop;
00871 }
00872 
00873 KPluginInfo::List Containment::listContainments(const QString &category,
00874                                                 const QString &parentApp)
00875 {
00876     QString constraint;
00877 
00878     if (parentApp.isEmpty()) {
00879         constraint.append("not exist [X-KDE-ParentApp]");
00880     } else {
00881         constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
00882     }
00883 
00884     if (!category.isEmpty()) {
00885         if (!constraint.isEmpty()) {
00886             constraint.append(" and ");
00887         }
00888 
00889         constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
00890         if (category == "Miscellaneous") {
00891             constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
00892         }
00893     }
00894 
00895     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
00896     //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches";
00897     return KPluginInfo::fromServices(offers);
00898 }
00899 
00900 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype)
00901 {
00902     QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
00903     //kDebug() << mimetype << constraint;
00904     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
00905     return KPluginInfo::fromServices(offers);
00906 }
00907 
00908 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00909 {
00910     //kDebug() << immutability() << Mutable << (immutability() == Mutable);
00911     event->setAccepted(immutability() == Mutable &&
00912                        (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) ||
00913                         KUrl::List::canDecode(event->mimeData())));
00914 
00915     if (!event->isAccepted()) {
00916         // check to see if we have an applet that accepts the format.
00917         QStringList formats = event->mimeData()->formats();
00918 
00919         foreach (const QString &format, formats) {
00920             KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(format);
00921             if (!appletList.isEmpty()) {
00922                 event->setAccepted(true);
00923                 break;
00924             }
00925         }
00926     }
00927 }
00928 
00929 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
00930 {
00931     QGraphicsItem *item = scene()->itemAt(event->scenePos());
00932     event->setAccepted(item == this || !item);
00933 }
00934 
00935 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event)
00936 {
00937     //kDebug() << event->mimeData()->text();
00938     if (!isContainment()) {
00939         Applet::dropEvent(event);
00940         return;
00941     }
00942 
00943     QString mimetype(static_cast<Corona*>(scene())->appletMimeType());
00944 
00945     if (event->mimeData()->hasFormat(mimetype) && scene()) {
00946         QString data = event->mimeData()->data(mimetype);
00947         QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
00948 
00949         foreach (const QString &appletName, appletNames) {
00950             //kDebug() << "doing" << appletName;
00951             QRectF geom(mapFromScene(event->scenePos()), QSize(0, 0));
00952             addApplet(appletName, QVariantList(), geom);
00953         }
00954         event->acceptProposedAction();
00955     } else if (KUrl::List::canDecode(event->mimeData())) {
00956         //TODO: collect the mimetypes of available script engines and offer
00957         //      to create widgets out of the matching URLs, if any
00958         KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
00959         foreach (const KUrl &url, urls) {
00960             KMimeType::Ptr mime = KMimeType::findByUrl(url);
00961             QString mimeName = mime->name();
00962             QRectF geom(event->pos(), QSize());
00963             QVariantList args;
00964             args << url.url();
00965             //             kDebug() << mimeName;
00966             KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(mimeName);
00967 
00968             if (!appletList.isEmpty()) {
00969                 //TODO: should we show a dialog here to choose which plasmoid load if
00971                 QMenu choices;
00972                 QHash<QAction *, QString> actionsToPlugins;
00973                 foreach (const KPluginInfo &info, appletList) {
00974                     QAction *action;
00975                     if (!info.icon().isEmpty()) {
00976                         action = choices.addAction(KIcon(info.icon()), info.name());
00977                     } else {
00978                         action = choices.addAction(info.name());
00979                     }
00980 
00981                     actionsToPlugins.insert(action, info.pluginName());
00982                 }
00983 
00984                 actionsToPlugins.insert(choices.addAction(i18n("Icon")), "icon");
00985                 QAction *choice = choices.exec(event->screenPos());
00986                 if (choice) {
00987                     addApplet(actionsToPlugins[choice], args, geom);
00988                 }
00989             } else if (url.protocol() != "data") {
00990                 // We don't try to do anything with data: URIs
00991                 // no special applet associated with this mimetype, let's
00992                 addApplet("icon", args, geom);
00993             }
00994         }
00995         event->acceptProposedAction();
00996     } else {
00997         QStringList formats = event->mimeData()->formats();
00998         QHash<QString, KPluginInfo> seenPlugins;
00999         QHash<QString, QString> pluginFormats;
01000 
01001         foreach (const QString &format, formats) {
01002             KPluginInfo::List plugins = Applet::listAppletInfoForMimetype(format);
01003 
01004             foreach (const KPluginInfo &plugin, plugins) {
01005                 if (seenPlugins.contains(plugin.pluginName())) {
01006                     continue;
01007                 }
01008 
01009                 seenPlugins.insert(plugin.pluginName(), plugin);
01010                 pluginFormats.insert(plugin.pluginName(), format);
01011             }
01012         }
01013 
01014         QString selectedPlugin;
01015 
01016         if (seenPlugins.isEmpty()) {
01017             // do nothing, we have no matches =/
01018         }
01019 
01020         if (seenPlugins.count() == 1) {
01021             selectedPlugin = seenPlugins.constBegin().key();
01022         } else {
01023             QMenu choices;
01024             QHash<QAction *, QString> actionsToPlugins;
01025             foreach (const KPluginInfo &info, seenPlugins) {
01026                 QAction *action;
01027                 if (!info.icon().isEmpty()) {
01028                     action = choices.addAction(KIcon(info.icon()), info.name());
01029                 } else {
01030                     action = choices.addAction(info.name());
01031                 }
01032 
01033                 actionsToPlugins.insert(action, info.pluginName());
01034             }
01035 
01036             QAction *choice = choices.exec(event->screenPos());
01037             if (choice) {
01038                 selectedPlugin = actionsToPlugins[choice];
01039             }
01040         }
01041 
01042         if (!selectedPlugin.isEmpty()) {
01043             KTemporaryFile tempFile;
01044             if (tempFile.open()) {
01045                 //TODO: what should we do with files after the applet is done with them??
01046                 tempFile.setAutoRemove(false);
01047 
01048                 {
01049                     QDataStream stream(&tempFile);
01050                     QByteArray data = event->mimeData()->data(pluginFormats[selectedPlugin]);
01051                     stream.writeRawData(data, data.size());
01052                 }
01053 
01054                 QRectF geom(event->pos(), QSize());
01055                 QVariantList args;
01056                 args << tempFile.fileName();
01057                 kDebug() << args;
01058                 tempFile.close();
01059 
01060                 addApplet(selectedPlugin, args, geom);
01061             }
01062         }
01063     }
01064 }
01065 
01066 const QGraphicsItem *Containment::toolBoxItem() const
01067 {
01068     return d->toolBox;
01069 }
01070 
01071 void Containment::resizeEvent(QGraphicsSceneResizeEvent *event)
01072 {
01073     Applet::resizeEvent(event);
01074     if (d->wallpaper) {
01075         d->wallpaper->setBoundingRect(boundingRect());
01076     }
01077 }
01078 
01079 void Containment::keyPressEvent(QKeyEvent *event)
01080 {
01081     //kDebug() << "keyPressEvent with" << event->key()
01082     //         << "and hoping and wishing for a" << Qt::Key_Tab;
01083     if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) {
01084         if (!d->applets.isEmpty()) {
01085             kDebug() << "let's give focus to...." << (QObject*)d->applets.first();
01086             d->applets.first()->setFocus(Qt::TabFocusReason);
01087         }
01088     }
01089 }
01090 
01091 void Containment::wheelEvent(QGraphicsSceneWheelEvent *event)
01092 {
01093     if (d->wallpaper && d->wallpaper->isInitialized()) {
01094         QGraphicsItem *item = scene()->itemAt(event->scenePos());
01095         if (item == this) {
01096             event->ignore();
01097             d->wallpaper->wheelEvent(event);
01098 
01099             if (event->isAccepted()) {
01100                 return;
01101             }
01102 
01103             event->accept();
01104         }
01105     }
01106 
01107     if (containmentType() == DesktopContainment) {
01108         QGraphicsItem *item = scene()->itemAt(event->scenePos());
01109         if (item == this) {
01110             int numDesktops = KWindowSystem::numberOfDesktops();
01111             int currentDesktop = KWindowSystem::currentDesktop();
01112 
01113             if (event->delta() < 0) {
01114                 KWindowSystem::setCurrentDesktop(currentDesktop % numDesktops + 1);
01115             } else {
01116                 KWindowSystem::setCurrentDesktop((numDesktops + currentDesktop - 2) % numDesktops + 1);
01117             }
01118 
01119             event->accept();
01120             return;
01121         }
01122     }
01123 
01124     event->ignore();
01125     Applet::wheelEvent(event);
01126 }
01127 
01128 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
01129 {
01130     Applet *applet = qgraphicsitem_cast<Applet*>(watched);
01131 
01132     // Otherwise we're watching something we shouldn't be...
01133     Q_ASSERT(applet != 0);
01134     if (!d->applets.contains(applet)) {
01135         return false;
01136     }
01137 
01138     //kDebug() << "got sceneEvent";
01139     switch (event->type()) {
01140     case QEvent::GraphicsSceneHoverEnter:
01141         //kDebug() << "got hoverenterEvent" << immutability() << " " << applet->immutability();
01142         if (immutability() == Mutable && applet->immutability() == Mutable) {
01143             QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
01144             if (d->handles.contains(applet)) {
01145                 AppletHandle *handle = d->handles.value(applet);
01146                 if (handle) {
01147                     handle->setHoverPos(he->pos());
01148                 }
01149             } else {
01150                 //kDebug() << "generated applet handle";
01151                 AppletHandle *handle = new AppletHandle(this, applet, he->pos());
01152                 d->handles[applet] = handle;
01153                 connect(handle, SIGNAL(disappearDone(AppletHandle*)),
01154                         this, SLOT(handleDisappeared(AppletHandle*)));
01155                 connect(applet, SIGNAL(geometryChanged()),
01156                         handle, SLOT(appletResized()));
01157             }
01158         }
01159         break;
01160     default:
01161         break;
01162     }
01163 
01164     return false;
01165 }
01166 
01167 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value)
01168 {
01169     //FIXME if the applet is moved to another containment we need to unfocus it
01170 
01171     if (isContainment() && !ContainmentPrivate::s_positioning &&
01172         (change == QGraphicsItem::ItemSceneHasChanged || change == QGraphicsItem::ItemPositionHasChanged)) {
01173         switch (containmentType()) {
01174             case PanelContainment:
01175             case CustomPanelContainment:
01176                 d->positionPanel();
01177                 break;
01178             default:
01179                 d->positionContainments();
01180                 break;
01181         }
01182     }
01183 
01184     return Applet::itemChange(change, value);
01185 }
01186 
01187 void Containment::enableAction(const QString &name, bool enable)
01188 {
01189     QAction *action = this->action(name);
01190     if (action) {
01191         action->setEnabled(enable);
01192         action->setVisible(enable);
01193     }
01194 }
01195 
01196 void Containment::addToolBoxAction(QAction *action)
01197 {
01198     if (d->toolBox) {
01199         d->toolBox->addTool(action);
01200     }
01201 }
01202 
01203 void Containment::removeToolBoxAction(QAction *action)
01204 {
01205     if (d->toolBox) {
01206         d->toolBox->removeTool(action);
01207     }
01208 }
01209 
01210 void Containment::setToolBoxOpen(bool open)
01211 {
01212     if (open) {
01213         openToolBox();
01214     } else {
01215         closeToolBox();
01216     }
01217 }
01218 
01219 void Containment::openToolBox()
01220 {
01221     if (d->toolBox) {
01222         d->toolBox->showToolBox();
01223     }
01224 }
01225 
01226 void Containment::closeToolBox()
01227 {
01228     if (d->toolBox) {
01229         d->toolBox->hideToolBox();
01230     }
01231 }
01232 
01233 void Containment::addAssociatedWidget(QWidget *widget)
01234 {
01235     Applet::addAssociatedWidget(widget);
01236     if (d->focusedApplet) {
01237         d->focusedApplet->addAssociatedWidget(widget);
01238     }
01239 
01240     foreach (const Applet *applet, d->applets) {
01241         if (applet->d->activationAction) {
01242             widget->addAction(applet->d->activationAction);
01243         }
01244     }
01245 }
01246 
01247 void Containment::removeAssociatedWidget(QWidget *widget)
01248 {
01249     Applet::removeAssociatedWidget(widget);
01250     if (d->focusedApplet) {
01251         d->focusedApplet->removeAssociatedWidget(widget);
01252     }
01253 
01254     foreach (const Applet *applet, d->applets) {
01255         if (applet->d->activationAction) {
01256             widget->removeAction(applet->d->activationAction);
01257         }
01258     }
01259 }
01260 
01261 void Containment::setDrawWallpaper(bool drawWallpaper)
01262 {
01263     d->drawWallpaper = drawWallpaper;
01264     if (drawWallpaper) {
01265         KConfigGroup cfg = config();
01266         QString wallpaper = cfg.readEntry("wallpaperplugin", defaultWallpaper);
01267         QString mode = cfg.readEntry("wallpaperpluginmode", defaultWallpaperMode);
01268         setWallpaper(wallpaper, mode);
01269     } else {
01270         delete d->wallpaper;
01271         d->wallpaper = 0;
01272     }
01273 }
01274 
01275 bool Containment::drawWallpaper()
01276 {
01277     return d->drawWallpaper;
01278 }
01279 
01280 void Containment::setWallpaper(const QString &pluginName, const QString &mode)
01281 {
01282     KConfigGroup cfg = config();
01283     bool newPlugin = true;
01284     bool newMode = true;
01285 
01286     if (d->drawWallpaper) {
01287         if (d->wallpaper) {
01288             // we have a wallpaper, so let's decide whether we need to swap it out
01289             if (d->wallpaper->pluginName() != pluginName) {
01290                 delete d->wallpaper;
01291                 d->wallpaper = 0;
01292             } else {
01293                 // it's the same plugin, so let's save its state now so when
01294                 // we call restore later on we're safe
01295                 newMode = d->wallpaper->renderingMode().name() != mode;
01296                 newPlugin = false;
01297             }
01298         }
01299 
01300         if (!pluginName.isEmpty() && !d->wallpaper) {
01301             d->wallpaper = Plasma::Wallpaper::load(pluginName);
01302         }
01303 
01304         if (d->wallpaper) {
01305             d->wallpaper->setBoundingRect(boundingRect());
01306             d->wallpaper->setRenderingMode(mode);
01307 
01308             if (newPlugin) {
01309                 connect(d->wallpaper, SIGNAL(update(const QRectF&)),
01310                         this, SLOT(updateRect(const QRectF&)));
01311                 cfg.writeEntry("wallpaperplugin", pluginName);
01312             }
01313 
01314             if (d->wallpaper->isInitialized()) {
01315                 KConfigGroup wallpaperConfig = KConfigGroup(&cfg, "Wallpaper");
01316                 wallpaperConfig = KConfigGroup(&wallpaperConfig, pluginName);
01317                 d->wallpaper->restore(wallpaperConfig);
01318             }
01319 
01320             if (newMode) {
01321                 cfg.writeEntry("wallpaperpluginmode", mode);
01322             }
01323         }
01324 
01325         update();
01326     }
01327 
01328     if (!d->wallpaper) {
01329         cfg.deleteEntry("wallpaperplugin");
01330         cfg.deleteEntry("wallpaperpluginmode");
01331     }
01332 
01333     if (newPlugin || newMode) {
01334         emit configNeedsSaving();
01335     }
01336 }
01337 
01338 Plasma::Wallpaper *Containment::wallpaper() const
01339 {
01340     return d->wallpaper;
01341 }
01342 
01343 void Containment::setActivity(const QString &activity)
01344 {
01345     Context *context = d->context();
01346     if (context->currentActivity() != activity) {
01347         context->setCurrentActivity(activity);
01348 
01349         foreach (Applet *a, d->applets) {
01350             a->updateConstraints(ContextConstraint);
01351         }
01352 
01353         KConfigGroup c = config();
01354         c.writeEntry("activity", activity);
01355         emit configNeedsSaving();
01356     }
01357 }
01358 
01359 QString Containment::activity() const
01360 {
01361     return d->context()->currentActivity();
01362 }
01363 
01364 Context *ContainmentPrivate::context()
01365 {
01366     if (!con) {
01367         con = new Context(q);
01368         q->connect(con, SIGNAL(changed(Plasma::Context*)),
01369                    q, SIGNAL(contextChanged(Plasma::Context*)));
01370     }
01371 
01372     return con;
01373 }
01374 
01375 KActionCollection &ContainmentPrivate::actions()
01376 {
01377     return static_cast<Applet*>(q)->d->actions;
01378 }
01379 
01380 void ContainmentPrivate::focusApplet(Plasma::Applet *applet)
01381 {
01382     if (focusedApplet == applet) {
01383         return;
01384     }
01385 
01386     QList<QWidget *> widgets = actions().associatedWidgets();
01387     if (focusedApplet) {
01388         foreach (QWidget *w, widgets) {
01389             focusedApplet->removeAssociatedWidget(w);
01390         }
01391     }
01392 
01393     if (applet && applets.contains(applet)) {
01394         //kDebug() << "switching to" << applet->name();
01395         focusedApplet = applet;
01396         foreach (QWidget *w, widgets) {
01397             focusedApplet->addAssociatedWidget(w);
01398         }
01399 
01400         if (!focusedApplet->hasFocus()) {
01401             focusedApplet->setFocus(Qt::ShortcutFocusReason);
01402         }
01403     } else {
01404         focusedApplet = 0;
01405     }
01406 }
01407 
01408 void Containment::focusNextApplet()
01409 {
01410     if (d->applets.isEmpty()) {
01411         return;
01412     }
01413     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
01414     if (index >= d->applets.size()) {
01415         index = 0;
01416     }
01417     kDebug() << "index" << index;
01418     d->focusApplet(d->applets.at(index));
01419 }
01420 
01421 void Containment::focusPreviousApplet()
01422 {
01423     if (d->applets.isEmpty()) {
01424         return;
01425     }
01426     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
01427     if (index < 0) {
01428         index = d->applets.size() - 1;
01429     }
01430     kDebug() << "index" << index;
01431     d->focusApplet(d->applets.at(index));
01432 }
01433 
01434 void Containment::destroy()
01435 {
01436     destroy(true);
01437 }
01438 
01439 void Containment::showConfigurationInterface()
01440 {
01441     Applet::showConfigurationInterface();
01442 }
01443 
01444 void ContainmentPrivate::requestConfiguration()
01445 {
01446     emit q->configureRequested(q);
01447 }
01448 
01449 void Containment::destroy(bool confirm)
01450 {
01451     if (immutability() != Mutable) {
01452         return;
01453     }
01454 
01455     if (isContainment()) {
01456         //don't remove a desktop that's in use
01457         //FIXME: this should probably be based on whether any views care or not!
01458         //       sth like: foreach (view) { view->requires(this); }
01459         Q_ASSERT(corona());
01460         if (d->type != PanelContainment && d->type != CustomPanelContainment &&
01461             (d->screen != -1 || d->screen >= corona()->numScreens())) {
01462             kDebug() << (QObject*)this << "containment has a screen number?" << d->screen;
01463             return;
01464         }
01465 
01466         //FIXME maybe that %1 should be the containment type not the name
01467         if (!confirm ||
01468             KMessageBox::warningContinueCancel(
01469                 view(),
01470                 i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", name()),
01471                 i18nc("@title:window %1 is the name of the containment", "Remove %1", name()), KStandardGuiItem::remove()) == KMessageBox::Continue) {
01472             //clearApplets();
01473             Applet::destroy();
01474         }
01475     } else {
01476         Applet::destroy();
01477     }
01478 }
01479 
01480 void ContainmentPrivate::toggleDesktopImmutability()
01481 {
01482     if (q->corona()) {
01483         if (q->corona()->immutability() == Mutable) {
01484             q->corona()->setImmutability(UserImmutable);
01485         } else if (q->corona()->immutability() == UserImmutable) {
01486             q->corona()->setImmutability(Mutable);
01487         }
01488     } else {
01489         if (q->immutability() == Mutable) {
01490             q->setImmutability(UserImmutable);
01491         } else if (q->immutability() == UserImmutable) {
01492             q->setImmutability(Mutable);
01493         }
01494     }
01495 
01496     if (q->immutability() != Mutable) {
01497         QMap<Applet*, AppletHandle*> h = handles;
01498         handles.clear();
01499 
01500         foreach (AppletHandle *handle, h) {
01501             handle->disconnect(q);
01502             handle->deleteLater();
01503         }
01504     }
01505 
01506     //setLockToolText();
01507 }
01508 
01509 void ContainmentPrivate::zoomIn()
01510 {
01511     emit q->zoomRequested(q, Plasma::ZoomIn);
01512     positionToolBox();
01513 }
01514 
01515 void ContainmentPrivate::zoomOut()
01516 {
01517     emit q->zoomRequested(q, Plasma::ZoomOut);
01518     positionToolBox();
01519 }
01520 
01521 ToolBox *ContainmentPrivate::createToolBox()
01522 {
01523     if (!toolBox) {
01524         switch (type) {
01525         case Containment::PanelContainment:
01526             toolBox = new PanelToolBox(q);
01527             toolBox->setSize(22);
01528             toolBox->setIconSize(QSize(16, 16));
01529             if (q->immutability() != Mutable) {
01530                 toolBox->hide();
01531             }
01532             break;
01533         case Containment::DesktopContainment:
01534             toolBox = new DesktopToolBox(q);
01535             break;
01536         default:
01537             break;
01538         }
01539 
01540         if (toolBox) {
01541             QObject::connect(toolBox, SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
01542             toolBox->load();
01543             positionToolBox();
01544         }
01545     }
01546 
01547     return toolBox;
01548 }
01549 
01550 void ContainmentPrivate::positionToolBox()
01551 {
01552     if (toolBox) {
01553         toolBox->reposition();
01554     }
01555 }
01556 
01557 void ContainmentPrivate::triggerShowAddWidgets()
01558 {
01559     emit q->showAddWidgetsInterface(QPointF());
01560 }
01561 
01562 void ContainmentPrivate::handleDisappeared(AppletHandle *handle)
01563 {
01564     if (handles.contains(handle->applet())) {
01565         handles.remove(handle->applet());
01566         handle->detachApplet();
01567         handle->deleteLater();
01568     }
01569 }
01570 
01571 void ContainmentPrivate::checkRemoveAction()
01572 {
01573     q->enableAction("remove", (q->immutability() == Mutable &&
01574                               (screen == -1 ||
01575                               type == Plasma::Containment::PanelContainment ||
01576                               type == Plasma::Containment::CustomPanelContainment)));
01577 }
01578 
01579 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
01580 {
01581     if (!q->isContainment()) {
01582         return;
01583     }
01584 
01585     //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox;
01586     if (constraints & Plasma::ImmutableConstraint) {
01587         //update actions
01588         checkRemoveAction();
01589         bool unlocked = q->immutability() == Mutable;
01590         q->setAcceptDrops(unlocked);
01591         q->enableAction("add widgets", unlocked);
01592         //FIXME immutability changes conflict with zoom changes
01593         /*action = actions().action("add sibling containment");
01594         if (action) {
01595             action->setVisible(unlocked);
01596             action->setEnabled(unlocked);
01597         }*/
01598         QAction *action = actions().action("lock widgets");
01599         if (action) {
01600             action->setText(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets"));
01601             action->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked"));
01602         }
01603 
01604         bool canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked");
01605         if (canConfig) {
01606             action = actions().action("activity settings");
01607             if (action) {
01608                 action->setVisible(canConfig);
01609                 action->setEnabled(canConfig);
01610             }
01611         }
01612 
01613         // tell the applets too
01614         foreach (Applet *a, applets) {
01615             a->updateConstraints(ImmutableConstraint);
01616         }
01617 
01618         if (toolBox) {
01619             if (type == Containment::PanelContainment || type == Containment::CustomPanelContainment) {
01620                 toolBox->setVisible(unlocked);
01621             } else {
01622                 toolBox->setIsMovable(unlocked);
01623             }
01624         }
01625     }
01626 
01627     if (constraints & Plasma::FormFactorConstraint) {
01628         if (toolBox) {
01629             if (q->formFactor() == Vertical) {
01630                 toolBox->setCorner(ToolBox::Bottom);
01631                 //defaults to horizontal
01632             } else if (QApplication::layoutDirection() == Qt::RightToLeft) {
01633                 toolBox->setCorner(ToolBox::Left);
01634             } else {
01635                 toolBox->setCorner(ToolBox::Right);
01636             }
01637         }
01638 
01639         foreach (Applet *applet, applets) {
01640             applet->updateConstraints(Plasma::FormFactorConstraint);
01641         }
01642     }
01643 
01644     if (constraints & Plasma::SizeConstraint && !ContainmentPrivate::s_positioning) {
01645         switch (q->containmentType()) {
01646             case Containment::PanelContainment:
01647             case Containment::CustomPanelContainment:
01648                 positionPanel();
01649                 break;
01650             default:
01651                 positionContainments();
01652                 break;
01653         }
01654     }
01655 
01656     if (toolBox && (constraints & Plasma::SizeConstraint ||
01657                     constraints & Plasma::FormFactorConstraint ||
01658                     constraints & Plasma::ScreenConstraint ||
01659                     constraints & Plasma::StartupCompletedConstraint)) {
01660         //kDebug() << "Positioning toolbox";
01661         positionToolBox();
01662     }
01663 
01664     if (toolBox && constraints & Plasma::StartupCompletedConstraint) {
01665         toolBox->addTool(q->action("remove"));
01666         checkRemoveAction();
01667     }
01668 }
01669 
01670 Applet *ContainmentPrivate::addApplet(const QString &name, const QVariantList &args,
01671                                       const QRectF &appletGeometry, uint id, bool delayInit)
01672 {
01673     if (!q->isContainment()) {
01674         return 0;
01675     }
01676 
01677     if (!delayInit && q->immutability() != Mutable) {
01678         kDebug() << "addApplet for" << name << "requested, but we're currently immutable!";
01679         return 0;
01680     }
01681 
01682     QGraphicsView *v = q->view();
01683     if (v) {
01684         v->setCursor(Qt::BusyCursor);
01685     }
01686 
01687     Applet *applet = Applet::load(name, id, args);
01688     if (v) {
01689         v->unsetCursor();
01690     }
01691 
01692     if (!applet) {
01693         kDebug() << "Applet" << name << "could not be loaded.";
01694         applet = new Applet(0, QString(), id);
01695         applet->setFailedToLaunch(true, i18n("Could not find requested component: %1", name));
01696     }
01697 
01698     //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry();
01699 
01700     q->addApplet(applet, appletGeometry.topLeft(), delayInit);
01701     return applet;
01702 }
01703 
01704 bool ContainmentPrivate::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) const
01705 {
01706     foreach (Applet *applet, applets) {
01707         if (applet != ignoredApplet && applet->geometry().intersects(region)) {
01708             return false;
01709         }
01710     }
01711     return true;
01712 }
01713 
01714 void ContainmentPrivate::appletDestroyed(QObject *object)
01715 {
01716     // we do a static_cast here since it really isn't an Applet by this
01717     // point anymore since we are in the qobject dtor. we don't actually
01718     // try and do anything with it, we just need the value of the pointer
01719     // so this unsafe looking code is actually just fine.
01720     //
01721     // NOTE: DO NOT USE THE applet VARIABLE FOR ANYTHING OTHER THAN COMPARING
01722     //       THE ADDRESS! ACTUALLY USING THE OBJECT WILL RESULT IN A CRASH!!!
01723     Applet *applet = static_cast<Plasma::Applet*>(object);
01724     applets.removeAll(applet);
01725     if (focusedApplet == applet) {
01726         focusedApplet = 0;
01727     }
01728 
01729     if (handles.contains(applet)) {
01730         AppletHandle *handle = handles.value(applet);
01731         handles.remove(applet);
01732         handle->deleteLater();
01733     }
01734 
01735     emit q->appletRemoved(applet);
01736     emit q->configNeedsSaving();
01737 }
01738 
01739 void ContainmentPrivate::containmentAppletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim)
01740 {
01741     if (anim == Animator::AppearAnimation &&
01742         q->containmentType() == Containment::DesktopContainment &&
01743         item->parentItem() == q) {
01744         Applet *applet = qgraphicsitem_cast<Applet*>(item);
01745 
01746         if (applet) {
01747             applet->installSceneEventFilter(q);
01748             KConfigGroup *cg = applet->d->mainConfigGroup();
01749             applet->save(*cg);
01750             emit q->configNeedsSaving();
01751             //applet->setWindowFlags(Qt::Window);
01752         }
01753     }
01754 }
01755 
01756 bool containmentSortByPosition(const Containment *c1, const Containment *c2)
01757 {
01758     return c1->id() < c2->id();
01759 }
01760 
01761 void ContainmentPrivate::positionContainments()
01762 {
01763     Corona *c = q->corona();
01764     if (!c || ContainmentPrivate::s_positioning) {
01765         return;
01766     }
01767 
01768     ContainmentPrivate::s_positioning = true;
01769 
01770     //TODO: we should avoid running this too often; consider compressing requests
01771     //      with a timer.
01772     QList<Containment*> containments = c->containments();
01773     QMutableListIterator<Containment*> it(containments);
01774 
01775     while (it.hasNext()) {
01776         Containment *containment = it.next();
01777         if (containment->containmentType() == Containment::PanelContainment ||
01778             containment->containmentType() == Containment::CustomPanelContainment) {
01779             // weed out all containments we don't care about at all
01780             // e.g. Panels and ourself
01781             it.remove();
01782             continue;
01783         }
01784     }
01785 
01786     if (containments.isEmpty()) {
01787         ContainmentPrivate::s_positioning = false;
01788         return;
01789     }
01790 
01791     qSort(containments.begin(), containments.end(), containmentSortByPosition);
01792     it.toFront();
01793 
01794     int column = 0;
01795     int x = 0;
01796     int y = 0;
01797     int rowHeight = 0;
01798     //int count = 0;
01799 
01800     //kDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++" << containments.count();
01801     while (it.hasNext()) {
01802         Containment *containment = it.next();
01803         containment->setPos(x, y);
01804         //kDebug() << ++count << "setting to" << x << y;
01805 
01806         int height = containment->size().height();
01807         if (height > rowHeight) {
01808             rowHeight = height;
01809         }
01810 
01811         ++column;
01812 
01813         if (column == CONTAINMENT_COLUMNS) {
01814             column = 0;
01815             x = 0;
01816             y += rowHeight + INTER_CONTAINMENT_MARGIN + TOOLBOX_MARGIN;
01817             rowHeight = 0;
01818         } else {
01819             x += containment->size().width() + INTER_CONTAINMENT_MARGIN;
01820         }
01821         //kDebug() << "column: " << column << "; x " << x << "; y" << y << "; width was"
01822         //         << containment->size().width();
01823     }
01824     //kDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++";
01825 
01826     ContainmentPrivate::s_positioning = false;
01827 }
01828 
01829 void ContainmentPrivate::positionPanel(bool force)
01830 {
01831     if (!q->scene()) {
01832         kDebug() << "no scene yet";
01833         return;
01834     }
01835 
01836     // we position panels in negative coordinates, and stack all horizontal
01837     // and all vertical panels with each other.
01838 
01839     const QPointF p = q->pos();
01840 
01841     if (!force &&
01842         p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
01843         q->scene()->collidingItems(q).isEmpty()) {
01844         // already positioned and not running into any other panels
01845         return;
01846     }
01847 
01848     //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here
01849     bool horiz = q->formFactor() == Plasma::Horizontal;
01850     qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
01851     qreal lastHeight = 0;
01852 
01853     // this should be ok for small numbers of panels, but if we ever end
01854     // up managing hundreds of them, this simplistic alogrithm will
01855     // likely be too slow.
01856     foreach (const Containment *other, q->corona()->containments()) {
01857         if (other == q ||
01858             (other->containmentType() != Containment::PanelContainment &&
01859              other->containmentType() != Containment::CustomPanelContainment) ||
01860             horiz != (other->formFactor() == Plasma::Horizontal)) {
01861             // only line up with panels of the same orientation
01862             continue;
01863         }
01864 
01865         if (horiz) {
01866             qreal y = other->pos().y();
01867             if (y < bottom) {
01868                 lastHeight = other->size().height();
01869                 bottom = y;
01870             }
01871         } else {
01872             qreal width = other->size().width();
01873             qreal x = other->pos().x() + width;
01874             if (x > bottom) {
01875                 lastHeight = width;
01876                 bottom = x + lastHeight;
01877             }
01878         }
01879     }
01880 
01881     kDebug() << "positioning" << (horiz ? "" : "non-") << "horizontal panel; forced?" << force;
01882     // give a space equal to the height again of the last item so there is
01883     // room to grow.
01884     QPointF newPos;
01885     if (horiz) {
01886         bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
01887         //TODO: fix x position for non-flush-left panels
01888         kDebug() << "moved to" << QPointF(0, bottom - q->size().height());
01889         newPos = QPointF(0, bottom - q->size().height());
01890     } else {
01891         bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
01892         //TODO: fix y position for non-flush-top panels
01893         kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
01894         newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
01895     }
01896 
01897     ContainmentPrivate::s_positioning = true;
01898     if (p != newPos) {
01899         q->setPos(newPos);
01900         emit q->geometryChanged();
01901     }
01902     ContainmentPrivate::s_positioning = false;
01903 }
01904 
01905 } // Plasma namespace
01906 
01907 #include "containment.moc"
01908 

Plasma

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

kdelibs

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