• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

libplasma

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 "containment_p.h"
00023 
00024 #include <QAction>
00025 #include <QDesktopWidget>
00026 #include <QFile>
00027 #include <QGraphicsSceneContextMenuEvent>
00028 #include <QGraphicsView>
00029 #include <QMimeData>
00030 #include <QPainter>
00031 #include <QStyleOptionGraphicsItem>
00032 #include <QGraphicsLayout>
00033 #include <QGraphicsLinearLayout>
00034 
00035 #include <KAction>
00036 #include <KApplication>
00037 #include <KAuthorized>
00038 #include <KIcon>
00039 #include <KMenu>
00040 #include <KMessageBox>
00041 #include <KMimeType>
00042 #include <KRun>
00043 #include <KServiceTypeTrader>
00044 #include <KStandardDirs>
00045 #include <KWindowSystem>
00046 
00047 #include "applet_p.h"
00048 #include "applethandle_p.h"
00049 #include "corona.h"
00050 #include "animator.h"
00051 #include "desktoptoolbox_p.h"
00052 #include "paneltoolbox_p.h"
00053 #include "svg.h"
00054 
00055 namespace Plasma
00056 {
00057 
00058 Containment::StyleOption::StyleOption()
00059     : QStyleOptionGraphicsItem(),
00060       view(0)
00061 {
00062     version = Version;
00063     type = Type;
00064 }
00065 
00066 Containment::StyleOption::StyleOption(const Containment::StyleOption & other)
00067     : QStyleOptionGraphicsItem(other),
00068       view(other.view)
00069 {
00070     version = Version;
00071     type = Type;
00072 }
00073 
00074 Containment::StyleOption::StyleOption(const QStyleOptionGraphicsItem &other)
00075     : QStyleOptionGraphicsItem(other),
00076       view(0)
00077 {
00078     version = Version;
00079     type = Type;
00080 }
00081 
00082 Containment::Containment(QGraphicsItem* parent,
00083                          const QString& serviceId,
00084                          uint containmentId)
00085     : Applet(parent, serviceId, containmentId),
00086       d(new ContainmentPrivate(this))
00087 {
00088     // WARNING: do not access config() OR globalConfig() in this method!
00089     //          that requires a scene, which is not available at this point
00090     setPos(0, 0);
00091     setBackgroundHints(NoBackground);
00092     setContainmentType(CustomContainment);
00093 }
00094 
00095 Containment::Containment(QObject* parent, const QVariantList& args)
00096     : Applet(parent, args),
00097       d(new ContainmentPrivate(this))
00098 {
00099     // WARNING: do not access config() OR globalConfig() in this method!
00100     //          that requires a scene, which is not available at this point
00101     setPos(0, 0);
00102     setBackgroundHints(NoBackground);
00103 }
00104 
00105 Containment::~Containment()
00106 {
00107     delete d;
00108 }
00109 
00110 void Containment::init()
00111 {
00112     if (isContainment()) {
00113         setCacheMode(NoCache);
00114         setFlag(QGraphicsItem::ItemIsMovable, false);
00115     }
00116     setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
00117     setAcceptDrops(true);
00118     setAcceptsHoverEvents(true);
00119 
00120     //TODO: would be nice to not do this on init, as it causes Animator to init
00121     connect(Animator::self(), SIGNAL(animationFinished(QGraphicsItem*,Plasma::Animator::Animation)),
00122             this, SLOT(containmentAppletAnimationComplete(QGraphicsItem*,Plasma::Animator::Animation)));
00123 
00124     if (d->type == NoContainmentType) {
00125         setContainmentType(DesktopContainment);
00126     }
00127 
00128     //common actions
00129     bool unlocked = immutability() == Mutable;
00130 
00131     QAction *appletBrowserAction = new QAction(i18n("Add Widgets..."), this);
00132     appletBrowserAction->setIcon(KIcon("list-add"));
00133     appletBrowserAction->setVisible(unlocked);
00134     appletBrowserAction->setEnabled(unlocked);
00135     connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets()));
00136     appletBrowserAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00137     appletBrowserAction->setShortcut(QKeySequence("ctrl+a"));
00138     d->actions().addAction("add widgets", appletBrowserAction);
00139 
00140     QAction *action = new QAction(i18n("Next Applet"), this);
00141     //no icon
00142     connect(action, SIGNAL(triggered()), this, SLOT(focusNextApplet()));
00143     action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00144     action->setShortcut(QKeySequence("ctrl+n"));
00145     d->actions().addAction("next applet", action);
00146 
00147     action = new QAction(i18n("Previous Applet"), this);
00148     //no icon
00149     connect(action, SIGNAL(triggered()), this, SLOT(focusPreviousApplet()));
00150     action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00151     action->setShortcut(QKeySequence("ctrl+p"));
00152     d->actions().addAction("previous applet", action);
00153 
00154     if (immutability() != SystemImmutable) {
00155         //FIXME I'm not certain this belongs in Containment
00156         //but it sure is nice to have the keyboard shortcut in every containment by default
00157         QAction *lockDesktopAction = new QAction(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets"), this);
00158         lockDesktopAction->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked"));
00159         connect(lockDesktopAction, SIGNAL(triggered(bool)), this, SLOT(toggleDesktopImmutability()));
00160         lockDesktopAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00161         lockDesktopAction->setShortcut(QKeySequence("ctrl+l"));
00162         d->actions().addAction("lock widgets", lockDesktopAction);
00163     }
00164 
00165     if (d->type != PanelContainment &&
00166         d->type != CustomPanelContainment) {
00167         QAction *zoomAction = new QAction(i18n("Zoom In"), this);
00168         zoomAction->setIcon(KIcon("zoom-in"));
00169         connect(zoomAction, SIGNAL(triggered(bool)), this, SLOT(zoomIn()));
00170         zoomAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00171         //two shortcuts because I hate ctrl-+ but others expect it
00172         QList<QKeySequence> keys;
00173         keys << QKeySequence(QKeySequence::ZoomIn);
00174         keys << QKeySequence("ctrl+=");
00175         zoomAction->setShortcuts(keys);
00176         d->actions().addAction("zoom in", zoomAction);
00177 
00178         zoomAction = new QAction(i18n("Zoom Out"), this);
00179         zoomAction->setIcon(KIcon("zoom-out"));
00180         connect(zoomAction, SIGNAL(triggered(bool)), this, SLOT(zoomOut()));
00181         zoomAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00182         zoomAction->setShortcut(QKeySequence(QKeySequence::ZoomOut));
00183         d->actions().addAction("zoom out", zoomAction);
00184 
00185         QAction *activityAction = new QAction(i18n("Add Activity"), this);
00186         activityAction->setIcon(KIcon("list-add"));
00187         activityAction->setVisible(unlocked);
00188         activityAction->setEnabled(unlocked);
00189         connect(activityAction, SIGNAL(triggered(bool)), this, SLOT(addSiblingContainment()));
00190         activityAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00191         activityAction->setShortcut(QKeySequence("ctrl+shift+a"));
00192         d->actions().addAction("add sibling containment", activityAction);
00193 
00194         if (d->type == DesktopContainment && d->toolBox) {
00195             d->toolBox->addTool(this->action("add widgets"));
00196             d->toolBox->addTool(this->action("zoom in"));
00197             d->toolBox->addTool(this->action("zoom out"));
00198             if (immutability() != SystemImmutable) {
00199                 d->toolBox->addTool(this->action("lock widgets"));
00200             }
00201             d->toolBox->addTool(this->action("add sibling containment"));
00202         }
00203     }
00204 
00205     Applet::init();
00206 }
00207 
00208 // helper function for sorting the list of applets
00209 bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2)
00210 {
00211     QPointF p1 = c1.readEntry("geometry", QRectF()).topLeft();
00212     QPointF p2 = c2.readEntry("geometry", QRectF()).topLeft();
00213     if (p1.x() != p2.x()) {
00214         return p1.x() < p2.x();
00215     }
00216     return p1.y() < p2.y();
00217 }
00218 
00219 void Containment::restore(KConfigGroup &group)
00220 {
00221     /*kDebug() << "!!!!!!!!!!!!initConstraints" << group.name() << containmentType();
00222     kDebug() << "    location:" << group.readEntry("location", (int)d->location);
00223     kDebug() << "    geom:" << group.readEntry("geometry", geometry());
00224     kDebug() << "    formfactor:" << group.readEntry("formfactor", (int)d->formFactor);
00225     kDebug() << "    screen:" << group.readEntry("screen", d->screen);*/
00226     if (!isContainment()) {
00227         Applet::restore(group);
00228         return;
00229     }
00230 
00231     QRectF geo = group.readEntry("geometry", geometry());
00232     //override max/min
00233     //this ensures panels are set to their saved size even when they have max & min set to prevent
00234     //resizing
00235     if (geo.size() != geo.size().boundedTo(maximumSize())) {
00236         setMaximumSize(maximumSize().expandedTo(geo.size()));
00237     }
00238     if (geo.size() != geo.size().expandedTo(minimumSize())) {
00239         setMinimumSize(minimumSize().boundedTo(geo.size()));
00240     }
00241     setGeometry(geo);
00242 
00243     setLocation((Plasma::Location)group.readEntry("location", (int)d->location));
00244     setFormFactor((Plasma::FormFactor)group.readEntry("formfactor", (int)d->formFactor));
00245     setScreen(group.readEntry("screen", d->screen));
00246 
00247     flushPendingConstraintsEvents();
00248     //kDebug() << "Containment" << id() << "geometry is" << geometry() << "config'd with" << appletConfig.name();
00249     restoreContents(group);
00250     setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
00251 }
00252 
00253 void Containment::save(KConfigGroup &g) const
00254 {
00255     KConfigGroup group = g;
00256     if (!group.isValid()) {
00257         group = config();
00258     }
00259 
00260     // locking is saved in Applet::save
00261     Applet::save(group);
00262     group.writeEntry("screen", d->screen);
00263     group.writeEntry("formfactor", (int)d->formFactor);
00264     group.writeEntry("location", (int)d->location);
00265     saveContents(group);
00266 }
00267 
00268 void Containment::saveContents(KConfigGroup &group) const
00269 {
00270     KConfigGroup applets(&group, "Applets");
00271     foreach (const Applet* applet, d->applets) {
00272         KConfigGroup appletConfig(&applets, QString::number(applet->id()));
00273         applet->save(appletConfig);
00274     }
00275 }
00276 
00277 void Containment::restoreContents(KConfigGroup &group)
00278 {
00279     KConfigGroup applets(&group, "Applets");
00280 
00281     // Sort the applet configs in order of geometry to ensure that applets
00282     // are added from left to right or top to bottom for a panel containment
00283     QList<KConfigGroup> appletConfigs;
00284     foreach (const QString &appletGroup, applets.groupList()) {
00285         //kDebug() << "reading from applet group" << appletGroup;
00286         KConfigGroup appletConfig(&applets, appletGroup);
00287         appletConfigs.append(appletConfig);
00288     }
00289     qSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan);
00290 
00291     foreach (KConfigGroup appletConfig, appletConfigs) {
00292         int appId = appletConfig.name().toUInt();
00293         //kDebug() << "the name is" << appletConfig.name();
00294         QString plugin = appletConfig.readEntry("plugin", QString());
00295 
00296         if (plugin.isEmpty()) {
00297             continue;
00298         }
00299 
00300         Applet *applet = d->addApplet(plugin, QVariantList(), appletConfig.readEntry("geometry", QRectF()), appId, true);
00301         applet->restore(appletConfig);
00302     }
00303 }
00304 
00305 Containment::Type Containment::containmentType() const
00306 {
00307     return d->type;
00308 }
00309 
00310 void Containment::setContainmentType(Containment::Type type)
00311 {
00312     if (d->type == type) {
00313         return;
00314     }
00315 
00316     delete d->toolBox;
00317     d->toolBox = 0;
00318     d->type = type;
00319 
00320     if (!isContainment()) {
00321         return;
00322     }
00323 
00324     if ((type == DesktopContainment || type == PanelContainment)) {
00325         d->createToolBox();
00326     }
00327 }
00328 
00329 Corona* Containment::corona() const
00330 {
00331     return dynamic_cast<Corona*>(scene());
00332 }
00333 
00334 void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
00335 {
00336     //kDebug() << "let's see if we manage to get a context menu here, huh";
00337     if (!isContainment() || !scene() || !KAuthorized::authorizeKAction("desktop_contextmenu")) {
00338         Applet::contextMenuEvent(event);
00339         return;
00340     }
00341 
00342     QPointF point = event->scenePos();
00343     QGraphicsItem* item = scene()->itemAt(point);
00344     if (item == this) {
00345         item = 0;
00346     }
00347 
00348     Applet* applet = 0;
00349 
00350     while (item) {
00351         applet = qgraphicsitem_cast<Applet*>(item);
00352         if (applet && !applet->isContainment()) {
00353             break;
00354         }
00355 
00356         // applet may have a value due to finding a containment!
00357         applet = 0;
00358         item = item->parentItem();
00359     }
00360 
00361     KMenu desktopMenu;
00362     //kDebug() << "context menu event " << (QObject*)applet;
00363     if (applet) {
00364         bool hasEntries = false;
00365 
00366         QList<QAction*> actions = applet->contextualActions();
00367         if (!actions.isEmpty()) {
00368             foreach(QAction* action, actions) {
00369                 if (action) {
00370                     desktopMenu.addAction(action);
00371                 }
00372             }
00373             hasEntries = true;
00374         }
00375 
00376         if (applet->hasConfigurationInterface()) {
00377             QAction* configureApplet = applet->d->actions.action("configure");
00378             if (configureApplet) {
00379                 desktopMenu.addAction(configureApplet);
00380                 hasEntries = true;
00381             }
00382         }
00383 
00384         QList<QAction*> containmentActions = contextualActions();
00385         if (!containmentActions.isEmpty()) {
00386             if (hasEntries) {
00387                 desktopMenu.addSeparator();
00388             }
00389 
00390             hasEntries = true;
00391             QMenu *containmentActionMenu = &desktopMenu;
00392 
00393             if (!actions.isEmpty() && containmentActions.count() > 2) {
00394                 containmentActionMenu = new KMenu(i18n("%1 Options", name()), &desktopMenu);
00395                 desktopMenu.addMenu(containmentActionMenu);
00396             }
00397 
00398             foreach (QAction* action, containmentActions) {
00399                 if (action) {
00400                     containmentActionMenu->addAction(action);
00401                 }
00402             }
00403         }
00404 
00405         if (scene() && static_cast<Corona*>(scene())->immutability() == Mutable) {
00406             if (hasEntries) {
00407                 desktopMenu.addSeparator();
00408             }
00409 
00410             QAction* closeApplet = applet->d->actions.action("remove");
00411             if (!closeApplet) { //unlikely but not impossible
00412                 kDebug() << "no remove action!!!!!!!!";
00413                 closeApplet = new QAction(i18n("Remove this %1", applet->name()), &desktopMenu);
00414                 closeApplet->setIcon(KIcon("edit-delete"));
00415                 connect(closeApplet, SIGNAL(triggered(bool)), applet, SLOT(destroy()));
00416             }
00417             desktopMenu.addAction(closeApplet);
00418             hasEntries = true;
00419         }
00420 
00421         if (!hasEntries) {
00422             Applet::contextMenuEvent(event);
00423             kDebug() << "no entries";
00424             return;
00425         }
00426     } else {
00427         if (!scene() || (static_cast<Corona*>(scene())->immutability() != Mutable && !KAuthorized::authorizeKAction("unlock_desktop"))) {
00428             //kDebug() << "immutability";
00429             Applet::contextMenuEvent(event);
00430             return;
00431         }
00432 
00433         QList<QAction*> actions = contextualActions();
00434 
00435         if (actions.count() < 1) {
00436             //kDebug() << "no applet, but no actions";
00437             Applet::contextMenuEvent(event);
00438             return;
00439         }
00440 
00441         foreach (QAction* action, actions) {
00442             if (action) {
00443                 desktopMenu.addAction(action);
00444             }
00445         }
00446     }
00447 
00448     event->accept();
00449     //kDebug() << "executing at" << event->screenPos();
00450     desktopMenu.exec(event->screenPos());
00451 }
00452 
00453 void Containment::setFormFactor(FormFactor formFactor)
00454 {
00455     if (d->formFactor == formFactor && layout()) {
00456         return;
00457     }
00458 
00459     //kDebug() << "switching FF to " << formFactor;
00460     FormFactor was = d->formFactor;
00461     d->formFactor = formFactor;
00462 
00463     if (isContainment() &&
00464         was != formFactor &&
00465         (d->type == PanelContainment ||
00466          d->type == CustomPanelContainment)) {
00467         // we are a panel and we have chaged our orientation
00468         d->positionPanel(true);
00469     }
00470 
00471     updateConstraints(Plasma::FormFactorConstraint);
00472 }
00473 
00474 void Containment::setLocation(Location location)
00475 {
00476     if (d->location == location) {
00477         return;
00478     }
00479 
00480     bool emitGeomChange = false;
00481 
00482     if ((location == TopEdge || location == BottomEdge) &&
00483         (d->location == TopEdge || d->location == BottomEdge)) {
00484         emitGeomChange = true;
00485     }
00486 
00487     if ((location == RightEdge || location == LeftEdge) &&
00488         (d->location == RightEdge || d->location == LeftEdge)) {
00489         emitGeomChange = true;
00490     }
00491 
00492     d->location = location;
00493 
00494     foreach (Applet* applet, d->applets) {
00495         applet->updateConstraints(Plasma::LocationConstraint);
00496     }
00497 
00498     if (emitGeomChange) {
00499         // our geometry on the scene will not actually change,
00500         // but for the purposes of views it has
00501         emit geometryChanged();
00502     }
00503 
00504     updateConstraints(Plasma::LocationConstraint);
00505 }
00506 
00507 void Containment::addSiblingContainment()
00508 {
00509     emit addSiblingContainment(this);
00510 }
00511 
00512 void Containment::clearApplets()
00513 {
00514     foreach (Applet *applet, d->applets) {
00515         applet->d->cleanUpAndDelete();
00516     }
00517 
00518     d->applets.clear();
00519 }
00520 
00521 Applet* Containment::addApplet(const QString& name, const QVariantList& args, const QRectF &appletGeometry)
00522 {
00523     return d->addApplet(name, args, appletGeometry);
00524 }
00525 
00526 //pos must be relative to the containment already. use mapfromscene.
00527 //what we're trying to do here for panels is make the applet go to the requested position,
00528 //or somewhere close to it, and get integrated properly into the containment as if it were created
00529 //there.
00530 void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit)
00531 {
00532     if (!delayInit && immutability() != Mutable) {
00533         return;
00534     }
00535 
00536     if (!applet) {
00537         kDebug() << "adding null applet!?!";
00538         return;
00539     }
00540 
00541     if (d->applets.contains(applet)) {
00542         kDebug() << "already have this applet!";
00543     }
00544 
00545     Containment *currentContainment = applet->containment();
00546 
00547     if (containmentType() == PanelContainment) {
00548         //panels don't want backgrounds, which is important when setting geometry
00549         setBackgroundHints(NoBackground);
00550     }
00551 
00552     if (currentContainment && currentContainment != this) {
00553         emit currentContainment->appletRemoved(applet);
00554         applet->removeSceneEventFilter(currentContainment);
00555         KConfigGroup oldConfig = applet->config();
00556         currentContainment->d->applets.removeAll(applet);
00557         if (currentContainment->d->handles.contains(applet)) {
00558             currentContainment->d->handles.remove(applet);
00559         }
00560         applet->setParentItem(this);
00561 
00562         // now move the old config to the new location
00563         KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
00564         oldConfig.reparent(&c);
00565         applet->d->resetConfigurationObject();
00566     } else {
00567         applet->setParentItem(this);
00568     }
00569 
00570     d->applets << applet;
00571 
00572     connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving()));
00573     connect(applet, SIGNAL(releaseVisualFocus()), this, SIGNAL(releaseVisualFocus()));
00574     connect(applet, SIGNAL(destroyed(QObject*)), this, SLOT(appletDestroyed(QObject*)));
00575 
00576     if (pos != QPointF(-1, -1)) {
00577         applet->setPos(pos);
00578     }
00579 
00580     if (delayInit) {
00581         if (containmentType() == DesktopContainment) {
00582             applet->installSceneEventFilter(this);
00583             //applet->setWindowFlags(Qt::Window);
00584         }
00585     } else {
00586         applet->init();
00587         Animator::self()->animateItem(applet, Animator::AppearAnimation);
00588     }
00589 
00590     applet->updateConstraints(Plasma::AllConstraints | Plasma::StartupCompletedConstraint);
00591     if (!delayInit) {
00592         applet->flushPendingConstraintsEvents();
00593     }
00594 
00595     emit appletAdded(applet, pos);
00596 }
00597 
00598 Applet::List Containment::applets() const
00599 {
00600     return d->applets;
00601 }
00602 
00603 void Containment::setScreen(int screen)
00604 {
00605     // screen of -1 means no associated screen.
00606     if (d->type == DesktopContainment || d->type == CustomContainment) {
00607 #ifndef Q_OS_WIN
00608         // we want to listen to changes in work area if our screen changes
00609         if (d->screen < 0 && screen > -1) {
00610             connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(positionToolBox()));
00611         } else if (screen < 0) {
00612             disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(positionToolBox()));
00613         }
00614 #endif
00615         if (screen > -1 && corona()) {
00616             // sanity check to make sure someone else doesn't have this screen already!
00617             Containment* currently = corona()->containmentForScreen(screen);
00618             if (currently && currently != this) {
00619                 //kDebug() << "currently is on screen" << currently->screen() << "and is" << currently->name() << (QObject*)currently << (QObject*)this;
00620                 currently->setScreen(-1);
00621             }
00622         }
00623     }
00624 
00625     //kDebug() << "setting screen to" << screen << "and we are a" << containmentType();
00626     QDesktopWidget *desktop = QApplication::desktop();
00627     int numScreens = desktop->numScreens();
00628     if (screen < -1) {
00629         screen = -1;
00630     }
00631 
00632     //kDebug() << "setting screen to " << screen << "and type is" << containmentType();
00633     if (screen < numScreens && screen > -1) {
00634         if (containmentType() == DesktopContainment ||
00635             containmentType() >= CustomContainment) {
00636             resize(desktop->screenGeometry(screen).size());
00637         }
00638     }
00639 
00640     int oldScreen = d->screen;
00641     d->screen = screen;
00642     updateConstraints(Plasma::ScreenConstraint);
00643     if (oldScreen != screen) {
00644         emit screenChanged(oldScreen, screen, this);
00645     }
00646 }
00647 
00648 int Containment::screen() const
00649 {
00650     return d->screen;
00651 }
00652 
00653 QPoint Containment::effectiveScreenPos() const
00654 {
00655     if (d->screen < 0) {
00656         return QPoint();
00657     }
00658 
00659     QRect r = QApplication::desktop()->screenGeometry(d->screen);
00660     if (containmentType() == PanelContainment ||
00661         containmentType() == CustomPanelContainment) {
00662         QRectF p = geometry();
00663 
00664         switch (d->location) {
00665             case TopEdge:
00666                 return QPoint(r.left() + p.x(), r.top());
00667                 break;
00668             case BottomEdge:
00669                 return QPoint(r.left() + p.x(), r.bottom() - p.height());
00670                 break;
00671             case LeftEdge:
00672                 return QPoint(r.left(), r.top() + (p.bottom() + INTER_CONTAINMENT_MARGIN));
00673                 break;
00674             case RightEdge:
00675                 return QPoint(r.right() - p.width(), r.top() + (p.bottom() + INTER_CONTAINMENT_MARGIN));
00676                 break;
00677             default:
00678                 //FIXME: implement properly for Floating!
00679                 return p.topLeft().toPoint();
00680                 break;
00681         }
00682     } else {
00683         //NOTE: if we ever support non-origin'd desktop containments
00684         //      this assumption here will have to change
00685         return r.topLeft();
00686     }
00687 }
00688 
00689 KPluginInfo::List Containment::listContainments(const QString &category,
00690                                                 const QString &parentApp)
00691 {
00692     QString constraint;
00693 
00694     if (parentApp.isEmpty()) {
00695         constraint.append("not exist [X-KDE-ParentApp]");
00696     } else {
00697         constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
00698     }
00699 
00700     if (!category.isEmpty()) {
00701         if (!constraint.isEmpty()) {
00702             constraint.append(" and ");
00703         }
00704 
00705         constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
00706         if (category == "Miscellaneous") {
00707             constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
00708         }
00709     }
00710 
00711     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
00712     //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches";
00713     return KPluginInfo::fromServices(offers);
00714 }
00715 
00716 KPluginInfo::List Containment::listContainmentsForMimetype(const QString &mimetype)
00717 {
00718     QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
00719     //kDebug() << mimetype << constraint;
00720     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
00721     return KPluginInfo::fromServices(offers);
00722 }
00723 
00724 void Containment::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00725 {
00726     //kDebug() << immutability() << Mutable << (immutability() == Mutable);
00727     event->setAccepted(immutability() == Mutable &&
00728                        (event->mimeData()->hasFormat(static_cast<Corona*>(scene())->appletMimeType()) ||
00729                         KUrl::List::canDecode(event->mimeData())));
00730 }
00731 
00732 void Containment::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
00733 {
00734     QGraphicsItem *item = scene()->itemAt(event->scenePos());
00735     event->setAccepted(item == this || !item);
00736 }
00737 
00738 void Containment::dropEvent(QGraphicsSceneDragDropEvent *event)
00739 {
00740     //kDebug() << event->mimeData()->text();
00741 
00742     QString mimetype(static_cast<Corona*>(scene())->appletMimeType());
00743 
00744     if (event->mimeData()->hasFormat(mimetype) && scene()) {
00745         QString data = event->mimeData()->data(mimetype);
00746         QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
00747 
00748         foreach (const QString &appletName, appletNames) {
00749             //kDebug() << "doing" << appletName;
00750             QRectF geom(mapFromScene(event->scenePos()), QSize(0, 0));
00751             addApplet(appletName, QVariantList(), geom);
00752         }
00753         event->acceptProposedAction();
00754     } else if (KUrl::List::canDecode(event->mimeData())) {
00755         //TODO: collect the mimetypes of available script engines and offer
00756         //      to create widgets out of the matching URLs, if any
00757         KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
00758         foreach (const KUrl& url, urls) {
00759             KMimeType::Ptr mime = KMimeType::findByUrl(url);
00760             QString mimeName = mime->name();
00761             QRectF geom(event->pos(), QSize());
00762             QVariantList args;
00763             args << url.url();
00764             //             kDebug() << mimeName;
00765             KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(mimeName);
00766 
00767             if (appletList.isEmpty()) {
00768                 // no special applet associated with this mimetype, let's
00769                 addApplet("icon", args, geom);
00770             } else {
00771                 //TODO: should we show a dialog here to choose which plasmoid load if
00773                 QMenu choices;
00774                 QHash<QAction*, QString> actionsToPlugins;
00775                 foreach (const KPluginInfo &info, appletList) {
00776                     QAction *action;
00777                     if (!info.icon().isEmpty()) {
00778                         action = choices.addAction(KIcon(info.icon()), info.name());
00779                     } else {
00780                         action = choices.addAction(info.name());
00781                     }
00782 
00783                     actionsToPlugins.insert(action, info.pluginName());
00784                 }
00785 
00786                 actionsToPlugins.insert(choices.addAction(i18n("Icon")), "icon");
00787                 QAction *choice = choices.exec(event->screenPos());
00788                 if (choice) {
00789                     addApplet(actionsToPlugins[choice], args, geom);
00790                 }
00791             }
00792         }
00793         event->acceptProposedAction();
00794     }
00795 }
00796 
00797 void Containment::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
00798 {
00799     //FIXME Qt4.4 check to see if this is still necessary to avoid unnecessary repaints
00800     //            check with QT_FLUSH_PAINT=1 and mouse through applets that accept hover,
00801     //            applets that don't and system windows
00802     if (event->spontaneous()) {
00803         Applet::hoverEnterEvent(event);
00804     }
00805     Q_UNUSED(event)
00806 }
00807 
00808 void Containment::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
00809 {
00810     //FIXME Qt4.4 check to see if this is still necessary to avoid unnecessary repaints
00811     //            check with QT_FLUSH_PAINT=1 and mouse through applets that accept hover,
00812     //            applets that don't and system windows
00813 //    Applet::hoverLeaveEvent(event);
00814     Q_UNUSED(event)
00815 }
00816 
00817 void Containment::keyPressEvent(QKeyEvent *event)
00818 {
00819     //kDebug() << "keyPressEvent with" << event->key() << "and hoping and wishing for a" << Qt::Key_Tab;
00820     if (event->key() == Qt::Key_Tab) { // && event->modifiers() == 0) {
00821         if (!d->applets.isEmpty()) {
00822             kDebug() << "let's give focus to...." << (QObject*)d->applets.first();
00823             d->applets.first()->setFocus(Qt::TabFocusReason);
00824         }
00825     }
00826 }
00827 
00828 bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
00829 {
00830     Applet *applet = qgraphicsitem_cast<Applet*>(watched);
00831 
00832     // Otherwise we're watching something we shouldn't be...
00833     Q_ASSERT(applet!=0);
00834     if (!d->applets.contains(applet)) {
00835         return false;
00836     }
00837 
00838     //kDebug() << "got sceneEvent";
00839     switch (event->type()) {
00840     case QEvent::GraphicsSceneHoverEnter:
00841         //kDebug() << "got hoverenterEvent" << immutability() << " " << applet->immutability();
00842         if (immutability() == Mutable && applet->immutability() == Mutable) {
00843             QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
00844             if (!d->handles.contains(applet)) {
00845                 //kDebug() << "generated applet handle";
00846                 AppletHandle *handle = new AppletHandle(this, applet, he->pos());
00847                 d->handles[applet] = handle;
00848                 connect(handle, SIGNAL(disappearDone(AppletHandle*)),
00849                         this, SLOT(handleDisappeared(AppletHandle*)));
00850                 connect(applet, SIGNAL(geometryChanged()),
00851                         handle, SLOT(appletResized()));
00852             }
00853         }
00854         break;
00855     default:
00856         break;
00857     }
00858 
00859     return false;
00860 }
00861 
00862 QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value)
00863 {
00864     //FIXME if the applet is moved to another containment we need to unfocus it
00865 
00866     if (isContainment() &&
00867         (change == QGraphicsItem::ItemSceneHasChanged || change == QGraphicsItem::ItemPositionHasChanged) &&
00868         !d->positioning) {
00869         switch (containmentType()) {
00870             case PanelContainment:
00871             case CustomPanelContainment:
00872                 d->positionPanel();
00873                 break;
00874             default:
00875                 d->positionContainment();
00876                 break;
00877         }
00878     }
00879 
00880     return Applet::itemChange(change, value);
00881 }
00882 
00883 void Containment::enableAction(const QString &name, bool enable)
00884 {
00885     QAction *action = this->action(name);
00886     if (action) {
00887         action->setEnabled(enable);
00888         action->setVisible(enable);
00889     }
00890 }
00891 
00892 void Containment::addToolBoxTool(QAction *action)
00893 {
00894     if (d->toolBox) {
00895         d->toolBox->addTool(action);
00896     }
00897 }
00898 
00899 void Containment::removeToolBoxTool(QAction *action)
00900 {
00901     if (d->toolBox) {
00902         d->toolBox->removeTool(action);
00903     }
00904 }
00905 
00906 void Containment::setToolBoxOpen(bool open)
00907 {
00908     if (open) {
00909         openToolBox();
00910     } else {
00911         closeToolBox();
00912     }
00913 }
00914 
00915 void Containment::openToolBox()
00916 {
00917     if (d->toolBox) {
00918         d->toolBox->showToolBox();
00919     }
00920 }
00921 
00922 void Containment::closeToolBox()
00923 {
00924     if (d->toolBox) {
00925         d->toolBox->hideToolBox();
00926     }
00927 }
00928 
00929 void Containment::addAssociatedWidget(QWidget *widget)
00930 {
00931     Applet::addAssociatedWidget(widget);
00932     if (d->focusedApplet) {
00933         d->focusedApplet->addAssociatedWidget(widget);
00934     }
00935 
00936     foreach (const Applet* applet, d->applets) {
00937         if (applet->d->activationAction) {
00938             widget->addAction(applet->d->activationAction);
00939         }
00940     }
00941 }
00942 
00943 void Containment::removeAssociatedWidget(QWidget *widget)
00944 {
00945     Applet::removeAssociatedWidget(widget);
00946     if (d->focusedApplet) {
00947         d->focusedApplet->removeAssociatedWidget(widget);
00948     }
00949 
00950     foreach (const Applet* applet, d->applets) {
00951         if (applet->d->activationAction) {
00952             widget->removeAction(applet->d->activationAction);
00953         }
00954     }
00955 }
00956 
00957 KActionCollection& ContainmentPrivate::actions()
00958 {
00959     return static_cast<Applet*>(q)->d->actions;
00960 }
00961 
00962 void ContainmentPrivate::focusApplet(Plasma::Applet *applet)
00963 {
00964     if (focusedApplet == applet) {
00965         return;
00966     }
00967 
00968     if (q->screen() == -1) {
00969         //what's the point of having focus if you're not on screen? :)
00970         //also, clicking a containment to switch to it makes the ZUI waay nicer.
00971         emit q->focusRequested(q);
00972     }
00973 
00974     QList<QWidget *> widgets = actions().associatedWidgets();
00975     if (focusedApplet) {
00976         foreach (QWidget *w, widgets) {
00977             focusedApplet->removeAssociatedWidget(w);
00978         }
00979     }
00980     //but what if applet isn't really one of our applets?
00981     //FIXME should we really unfocus the old applet?
00982     if (applets.contains(applet)) {
00983         //kDebug() << "switching to" << applet->name();
00984         focusedApplet = applet;
00985         if (focusedApplet) {
00986             foreach (QWidget *w, widgets) {
00987                 focusedApplet->addAssociatedWidget(w);
00988             }
00989         }
00990         applet->setFocus(Qt::ShortcutFocusReason);
00991     } else {
00992         focusedApplet = 0;
00993     }
00994 }
00995 
00996 void Containment::focusNextApplet()
00997 {
00998     if (d->applets.isEmpty()) {
00999         return;
01000     }
01001     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) + 1 : 0;
01002     if (index >= d->applets.size()) {
01003         index = 0;
01004     }
01005     kDebug() << "index" << index;
01006     d->focusApplet(d->applets.at(index));
01007 }
01008 
01009 void Containment::focusPreviousApplet()
01010 {
01011     if (d->applets.isEmpty()) {
01012         return;
01013     }
01014     int index = d->focusedApplet ? d->applets.indexOf(d->focusedApplet) - 1 : -1;
01015     if (index < 0) {
01016         index = d->applets.size() - 1;
01017     }
01018     kDebug() << "index" << index;
01019     d->focusApplet(d->applets.at(index));
01020 }
01021 
01022 void Containment::destroy()
01023 {
01024     if (immutability() != Mutable) {
01025         return;
01026     }
01027 
01028     if (isContainment()) {
01029         //don't remove a desktop that's in use
01030         //FIXME allow removal of containments for screens that don't currently exist
01031         if (d->type != PanelContainment && d->type != CustomPanelContainment &&
01032             (d->screen != -1 || d->screen >= QApplication::desktop()->numScreens())) {
01033             kDebug() << (QObject*)this << "containment has a screen number?" << d->screen;
01034             return;
01035         }
01036 
01037         //FIXME maybe that %1 should be the containment type not the name
01038         if (KMessageBox::warningContinueCancel(view(), i18n("Do you really want to remove this %1?", name()),
01039                     i18n("Remove %1", name()), KStandardGuiItem::remove()) == KMessageBox::Continue ) {
01040             //clearApplets();
01041             Applet::destroy();
01042         }
01043     } else {
01044         Applet::destroy();
01045     }
01046 }
01047 
01048 
01049 // Private class implementation
01050 
01051 void ContainmentPrivate::toggleDesktopImmutability()
01052 {
01053     if (q->corona()) {
01054         if (q->corona()->immutability() == Mutable) {
01055             q->corona()->setImmutability(UserImmutable);
01056         } else if (q->corona()->immutability() == UserImmutable) {
01057             q->corona()->setImmutability(Mutable);
01058         }
01059     } else {
01060         if (q->immutability() == Mutable) {
01061             q->setImmutability(UserImmutable);
01062         } else if (q->immutability() == UserImmutable) {
01063             q->setImmutability(Mutable);
01064         }
01065     }
01066 }
01067 
01068 void ContainmentPrivate::zoomIn()
01069 {
01070     emit q->zoomRequested(q, Plasma::ZoomIn);
01071 }
01072 
01073 void ContainmentPrivate::zoomOut()
01074 {
01075     emit q->zoomRequested(q, Plasma::ZoomOut);
01076 }
01077 
01078 ToolBox* ContainmentPrivate::createToolBox()
01079 {
01080     if (!toolBox) {
01081         switch (type) {
01082         case Containment::PanelContainment:
01083             toolBox = new PanelToolBox(q);
01084             toolBox->setSize(22);
01085             toolBox->setIconSize(QSize(16, 16));
01086             if (q->immutability() != Mutable) {
01087                 toolBox->hide();
01088             }
01089             break;
01090         case Containment::DesktopContainment:
01091             toolBox = new DesktopToolBox(q);
01092             break;
01093         default:
01094             break;
01095         }
01096 
01097         if (toolBox) {
01098             QObject::connect(toolBox, SIGNAL(toggled()), q, SIGNAL(toolBoxToggled()));
01099             positionToolBox();
01100         }
01101     }
01102 
01103     return toolBox;
01104 }
01105 
01106 void ContainmentPrivate::positionToolBox()
01107 {
01108     if (!toolBox) {
01109         return;
01110     }
01111 
01112     //The placement assumes that the geometry width/height is no more than the screen
01113     if (type == Containment::PanelContainment) {
01114         if (q->formFactor() == Vertical) {
01115             toolBox->setOrientation(Qt::Vertical);
01116             toolBox->setPos(q->geometry().width()/2 - toolBox->boundingRect().width()/2, q->geometry().height());
01117         //defaulting to Horizontal right now
01118         } else {
01119             toolBox->setOrientation(Qt::Horizontal);
01120             if (QApplication::layoutDirection() == Qt::RightToLeft) {
01121                 toolBox->setPos(q->geometry().left(), q->geometry().height()/2 - toolBox->boundingRect().height()/2);
01122             } else {
01123                 toolBox->setPos(q->geometry().width(), q->geometry().height()/2 - toolBox->boundingRect().height()/2);
01124             }
01125         }
01126     } else {
01127         QDesktopWidget *desktop = QApplication::desktop();
01128         QRectF r = desktop->availableGeometry(screen);
01129         if (q->view() && !q->view()->transform().isScaling()) {
01130             toolBox->setPos(r.topRight());
01131         } else {
01132             toolBox->setPos(q->mapFromScene(QPointF(q->geometry().topRight())));
01133         }
01134     }
01135 }
01136 
01137 void ContainmentPrivate::triggerShowAddWidgets()
01138 {
01139     emit q->showAddWidgetsInterface(QPointF());
01140 }
01141 
01142 void ContainmentPrivate::handleDisappeared(AppletHandle *handle)
01143 {
01144     if (handles.contains(handle->applet())) {
01145         handles.remove(handle->applet());
01146         handle->deleteLater();
01147     }
01148 }
01149 
01150 void ContainmentPrivate::containmentConstraintsEvent(Plasma::Constraints constraints)
01151 {
01152     if (!q->isContainment()) {
01153         return;
01154     }
01155 
01156     //kDebug() << "got containmentConstraintsEvent" << constraints << (QObject*)toolBox;
01157     if (constraints & Plasma::ImmutableConstraint) {
01158         //update actions
01159         bool unlocked = q->immutability() == Mutable;
01160         q->setAcceptDrops(unlocked);
01161 
01162         QAction *action = actions().action("add widgets");
01163         if (action) {
01164             action->setVisible(unlocked);
01165             action->setEnabled(unlocked);
01166         }
01167         //FIXME immutability changes conflict with zoom changes
01168         /*action = actions().action("add sibling containment");
01169         if (action) {
01170             action->setVisible(unlocked);
01171             action->setEnabled(unlocked);
01172         }*/
01173         action = actions().action("lock widgets");
01174         if (action) {
01175             action->setText(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets"));
01176             action->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked"));
01177         }
01178 
01179         // tell the applets too
01180         foreach (Applet *a, applets) {
01181             a->updateConstraints(ImmutableConstraint);
01182         }
01183 
01184         if (q->isContainment() && type == Containment::PanelContainment) {
01185             if (unlocked) {
01186                 toolBox->show();
01187             } else {
01188                 toolBox->hide();
01189             }
01190         }
01191     }
01192 
01193     if ((constraints & Plasma::SizeConstraint || constraints & Plasma::ScreenConstraint) &&
01194          toolBox) {
01195         positionToolBox();
01196     }
01197 
01198     if (constraints & Plasma::FormFactorConstraint) {
01199         if (toolBox) {
01200             if (q->formFactor() == Vertical) {
01201                 toolBox->setOrientation(Qt::Vertical);
01202                 //defaults to horizontal
01203             } else {
01204                 toolBox->setOrientation(Qt::Horizontal);
01205             }
01206         }
01207 
01208         foreach (Applet *applet, applets) {
01209             applet->updateConstraints(Plasma::FormFactorConstraint);
01210         }
01211     }
01212 
01213     if (constraints & Plasma::SizeConstraint) {
01214         switch (q->containmentType()) {
01215             case Containment::PanelContainment:
01216             case Containment::CustomPanelContainment:
01217                 positionPanel();
01218                 break;
01219             default:
01220                 positionContainment();
01221                 break;
01222         }
01223     }
01224 }
01225 
01226 Applet* ContainmentPrivate::addApplet(const QString& name, const QVariantList& args,
01227                                         const QRectF& appletGeometry, uint id, bool delayInit)
01228 {
01229     if (!delayInit && q->immutability() != Mutable) {
01230         kDebug() << "addApplet for" << name << "requested, but we're currently immutable!";
01231         return 0;
01232     }
01233 
01234     QGraphicsView *v = q->view();
01235     if (v) {
01236         v->setCursor(Qt::BusyCursor);
01237     }
01238 
01239     Applet* applet = Applet::load(name, id, args);
01240     if (v) {
01241         v->unsetCursor();
01242     }
01243 
01244     if (!applet) {
01245         kDebug() << "Applet" << name << "could not be loaded.";
01246         applet = new Applet(0, QString(), id);
01247         //TODO: uncomment this when not in string freeze.
01248         //applet->setFailedToLaunch(true, QString("Could not find requested component: %1").arg(name));
01249     }
01250 
01251     //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry();
01252 
01253     q->addApplet(applet, appletGeometry.topLeft(), delayInit);
01254     return applet;
01255 }
01256 
01257 bool ContainmentPrivate::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) const
01258 {
01259     foreach (Applet *applet, applets) {
01260         if (applet != ignoredApplet && applet->geometry().intersects(region)) {
01261             return false;
01262         }
01263     }
01264     return true;
01265 }
01266 
01267 void ContainmentPrivate::appletDestroyed(QObject* object)
01268 {
01269     // we do a static_cast here since it really isn't an Applet by this
01270     // point anymore since we are in the qobject dtor. we don't actually
01271     // try and do anything with it, we just need the value of the pointer
01272     // so this unsafe looking code is actually just fine.
01273     Applet* applet = static_cast<Plasma::Applet*>(object);
01274     applets.removeAll(applet);
01275     if (focusedApplet == applet) {
01276         focusedApplet = 0;
01277     }
01278     emit q->appletRemoved(applet);
01279     emit q->configNeedsSaving();
01280 }
01281 
01282 void ContainmentPrivate::containmentAppletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim)
01283 {
01284     if (anim == Animator::AppearAnimation &&
01285         q->containmentType() == Containment::DesktopContainment &&
01286         item->parentItem() == q) {
01287         Applet *applet = qgraphicsitem_cast<Applet*>(item);
01288 
01289         if (applet) {
01290             applet->installSceneEventFilter(q);
01291             KConfigGroup *cg = applet->d->mainConfigGroup();
01292             applet->save(*cg);
01293             emit q->configNeedsSaving();
01294             //applet->setWindowFlags(Qt::Window);
01295         }
01296     }
01297 }
01298 
01299 void ContainmentPrivate::positionContainment()
01300 {
01301     Corona *c = q->corona();
01302     if (!c) {
01303         return;
01304     }
01305 
01306     QList<Containment*> containments = c->containments();
01307     QMutableListIterator<Containment*> it(containments);
01308 
01309     bool noCollissions = true;
01310     while (it.hasNext()) {
01311         Containment *containment = it.next();
01312         if (containment == q ||
01313             containment->containmentType() == Containment::PanelContainment ||
01314             containment->containmentType() == Containment::CustomPanelContainment) {
01315             // weed out all containments we don't care about at all
01316             // e.g. Panels and ourself
01317             it.remove();
01318             continue;
01319         }
01320 
01321         if (noCollissions && q->collidesWithItem(containment)) {
01322             noCollissions = false;
01323         }
01324     }
01325 
01326     if (noCollissions) {
01327         // we made it all the way through the list, we have no
01328         // collisions
01329         return;
01330     }
01331 
01332     int width = 0;
01333     int height = 0;
01334 
01335     QDesktopWidget *desktop = QApplication::desktop();
01336     int numScreens = desktop->numScreens();
01337 
01338     for (int i = 0; i < numScreens; ++i) {
01339         QRect otherScreen = desktop->screenGeometry(i);
01340 
01341         if (width < otherScreen.width()) {
01342             width = otherScreen.width();
01343         }
01344 
01345         if (height < otherScreen.height()) {
01346             height = otherScreen.height();
01347         }
01348     }
01349 
01350     //this magic number (4) is the number of columns to try before going to the next row
01351     width = (width + INTER_CONTAINMENT_MARGIN) * 4;
01352     height += INTER_CONTAINMENT_MARGIN;
01353 
01354     // a mildly naive "find the first slot" approach
01355     QRectF r = q->boundingRect();
01356     QPointF topLeft(0, 0);
01357     q->setPos(topLeft);
01358 
01359     positioning = true;
01360     while (true) {
01361         it.toFront();
01362         int shift = 0;
01363 
01364         while (it.hasNext()) {
01365             Containment *containment = it.next();
01366 
01367             if (q->collidesWithItem(containment)) {
01368                 shift = containment->geometry().right();
01369                 /*kDebug() << (QObject*)q << "collides with" << (QObject*)containment
01370                          << containment->geometry() << "so shift to: " << shift;*/
01371                 //TODO: is it safe to remove a containment once we've
01372                 // collided with it?
01373                 break;
01374             }
01375 
01376             QPointF pos = containment->pos();
01377             if (pos.x() <= topLeft.x() && pos.y() <= topLeft.y()) {
01378                 // we don't colid with this containment, and it's above
01379                 // and to the left of us, so let's not bother checking
01380                 // it again if we go through this loop again
01381                 it.remove();
01382             }
01383         }
01384 
01385         if (shift == 0) {
01386             // success! no collisions!
01387             break;
01388         }
01389 
01390         if (shift + r.width() + INTER_CONTAINMENT_MARGIN > width) {
01391             // we ran out of width room, try another row
01392             topLeft = QPoint(0, topLeft.y() + height);
01393         } else {
01394             topLeft.setX(shift + INTER_CONTAINMENT_MARGIN);
01395         }
01396 
01397         q->setPos(topLeft);
01398         //kDebug() << "trying at" << topLeft << q->geometry();
01399         //kDebug() << collidingItems().count() << collidingItems()[0] << (QGraphicsItem*)this;
01400     }
01401 
01402     positioning = false;
01403 }
01404 
01405 void ContainmentPrivate::positionPanel(bool force)
01406 {
01407     if (!q->scene()) {
01408         kDebug() << "no scene yet";
01409         return;
01410     }
01411 
01412     // we position panels in negative coordinates, and stack all horizontal
01413     // and all vertical panels with each other.
01414 
01415     const QPointF p = q->pos();
01416 
01417     if (!force &&
01418         p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN &&
01419         q->scene()->collidingItems(q).isEmpty()) {
01420         // already positioned and not running into any other panels
01421         return;
01422     }
01423 
01424     //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here
01425     bool horiz = q->formFactor() == Plasma::Horizontal;
01426     qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET;
01427     qreal lastHeight = 0;
01428 
01429     // this should be ok for small numbers of panels, but if we ever end
01430     // up managing hundreds of them, this simplistic alogrithm will
01431     // likely be too slow.
01432     foreach (const Containment* other, q->corona()->containments()) {
01433         if (other == q ||
01434             (other->containmentType() != Containment::PanelContainment &&
01435              other->containmentType() != Containment::CustomPanelContainment) ||
01436             horiz != (other->formFactor() == Plasma::Horizontal)) {
01437             // only line up with panels of the same orientation
01438             continue;
01439         }
01440 
01441         if (horiz) {
01442             qreal y = other->pos().y();
01443             if (y < bottom) {
01444                 lastHeight = other->size().height();
01445                 bottom = y;
01446             }
01447         } else {
01448             qreal width = other->size().width();
01449             qreal x = other->pos().x() + width;
01450             if (x > bottom) {
01451                 lastHeight = width;
01452                 bottom = x + lastHeight;
01453             }
01454         }
01455     }
01456 
01457     kDebug() << "positioning" << (horiz ? "" : "non-") << "horizontal panel; forced?" << force;
01458     // give a space equal to the height again of the last item so there is
01459     // room to grow.
01460     QPointF newPos;
01461     if (horiz) {
01462         bottom -= lastHeight + INTER_CONTAINMENT_MARGIN;
01463         //TODO: fix x position for non-flush-left panels
01464         kDebug() << "moved to" << QPointF(0, bottom - q->size().height());
01465         newPos = QPointF(0, bottom - q->size().height());
01466     } else {
01467         bottom += lastHeight + INTER_CONTAINMENT_MARGIN;
01468         //TODO: fix y position for non-flush-top panels
01469         kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
01470         newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height());
01471     }
01472 
01473     positioning = true;
01474     if (p != newPos) {
01475         q->setPos(newPos);
01476         emit q->geometryChanged();
01477     }
01478     positioning = false;
01479 }
01480 
01481 
01482 } // Plasma namespace
01483 
01484 #include "containment.moc"
01485 

libplasma

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

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
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