Index: workspace/libs/plasma/applet.cpp
===================================================================
--- workspace/libs/plasma/applet.cpp	(revision 842464)
+++ workspace/libs/plasma/applet.cpp	(working copy)
@@ -1358,6 +1358,8 @@
 
 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value)
 {
+    QVariant ret = QGraphicsWidget::itemChange(change, value);
+
     //kDebug() << change;
     switch (change) {
     case ItemSceneHasChanged: {
@@ -1374,7 +1376,7 @@
         break;
     };
 
-    return QGraphicsWidget::itemChange(change, value);
+    return ret;
 }
 
 QPainterPath Applet::shape() const
Index: workspace/plasma/containments/desktop/desktoplayout.cpp
===================================================================
--- workspace/plasma/containments/desktop/desktoplayout.cpp	(revision 0)
+++ workspace/plasma/containments/desktop/desktoplayout.cpp	(revision 0)
@@ -0,0 +1,531 @@
+#include <limits>
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QGraphicsWidget>
+
+#include <KDebug>
+
+#include "desktoplayout.h"
+
+DesktopLayout::DesktopLayout (QGraphicsLayoutItem *parent)
+  : QGraphicsLayout(parent),
+    placementSpacing(defaultPlacementSpacing),
+    shiftingSpacing(defaultShiftingSpacing)
+{
+}
+
+DesktopLayout::~DesktopLayout ()
+{
+}
+
+void DesktopLayout::addItem (QGraphicsLayoutItem *item, QRectF preferredGeometry)
+{
+  DesktopLayoutItem newItem;
+  newItem.item = item;
+  newItem.preferredGeometry = preferredGeometry;
+  newItem.lastGeometry = QRectF(0, 0, -1, -1);
+  items.append(newItem);
+  invalidate();
+}
+
+void DesktopLayout::addItem (QGraphicsLayoutItem *item)
+{
+  addItem(item, QRectF(0, 0, -1, -1));
+}
+
+void DesktopLayout::setPlacementSpacing (qreal spacing)
+{
+  placementSpacing = spacing;
+}
+
+void DesktopLayout::setShiftingSpacing (qreal spacing)
+{
+  shiftingSpacing = spacing;
+}
+
+int DesktopLayout::count () const
+{
+  return items.size();
+}
+
+QGraphicsLayoutItem *DesktopLayout::itemAt (int i) const
+{
+  return items.at(i).item;
+}
+
+void DesktopLayout::removeAt (int i)
+{
+  items.removeAt(i);
+  invalidate();
+}
+
+// calculate the position where the item can be on the screen,
+// based on its preferred position and screen size
+QRectF DesktopLayout::limitPosition(const QRectF &preferred) const
+{
+  QRectF result = preferred;
+
+  // X position
+  if (preferred.left()-shiftingSpacing < 0) {
+    result.moveLeft(shiftingSpacing);
+  } else if (preferred.right()+shiftingSpacing > effectiveRect.width()) {
+    if (preferred.width()+shiftingSpacing > effectiveRect.width()) {
+      result.moveLeft(0);
+    } else {
+      result.moveRight(effectiveRect.width()-shiftingSpacing);
+    }
+  }
+
+  // Y position
+  if (preferred.top()-shiftingSpacing < 0) {
+    result.moveTop(shiftingSpacing);
+  } else if (preferred.bottom()+shiftingSpacing > effectiveRect.height()) {
+    if (preferred.height()+shiftingSpacing > effectiveRect.height()) {
+      result.moveTop(0);
+    } else {
+      result.moveBottom(effectiveRect.height()-shiftingSpacing);
+    }
+  }
+
+  return result;
+}
+
+// NOTE:
+// calling setGeometry on an item immediately causes itemGeometryChanged to be called,
+// so always first update all values and call setGeometry at the end
+
+// update anything that needs updating
+void DesktopLayout::setGeometry(const QRectF &rect)
+{
+  QGraphicsLayout::setGeometry(rect);
+  effectiveRect = geometry();
+
+  for (int i=0; i<items.size(); i++) {
+    DesktopLayoutItem &item = items[i];
+
+    if (!item.lastGeometry.isValid()) {
+      // position a new item
+      if (item.preferredGeometry.isValid()) {
+        item.lastGeometry = limitPosition(item.preferredGeometry);
+      } else {
+        //QSizeF itemSize = item.item->effectiveSizeHint(Qt::PreferredSize);
+        QSizeF itemSize = item.item->geometry().size();
+        QPointF newPos = positionVertically(itemSize, Top, Left, placementSpacing, placementSpacing, placementSpacing, placementSpacing);
+        if (newPos == QPointF(-1, -1)) {
+          newPos = QPointF(0, 0);
+        }
+        item.preferredGeometry = QRectF(newPos, itemSize);
+        item.lastGeometry = item.preferredGeometry;
+      }
+      kDebug() << "Placing" << i << "to" << item.lastGeometry << "preferred" << item.preferredGeometry;
+      item.item->setGeometry(item.lastGeometry);
+    } else if (item.item->geometry().isValid()) {
+      // check existing item's position
+      QRectF newGeom = limitPosition(item.preferredGeometry);
+      if (newGeom != item.item->geometry()) {
+        item.lastGeometry = newGeom;
+        kDebug() << "Shifting" << i << "to" << item.lastGeometry;
+        item.item->setGeometry(item.lastGeometry);
+      }
+    } else {
+      kDebug() << "ERROR geometry got invalid";
+    }
+  }
+}
+
+void DesktopLayout::itemGeometryChanged (QGraphicsLayoutItem *layoutItem)
+{
+  for (int i=0; i<items.size(); i++) {
+    DesktopLayoutItem &item = items[i];
+    if (item.item == layoutItem) {
+      if (item.lastGeometry.isValid() && item.lastGeometry != item.item->geometry()) {
+        item.preferredGeometry = item.item->geometry();
+        item.lastGeometry = limitPosition(item.preferredGeometry);
+        kDebug() << "Repositioned" << i << "to" << item.lastGeometry << "preferred" << item.preferredGeometry;
+        if (item.lastGeometry != item.preferredGeometry) {
+          item.item->setGeometry(item.lastGeometry);
+        }
+      }
+      return;
+    }
+  }
+}
+
+QSizeF DesktopLayout::sizeHint (Qt::SizeHint which, const QSizeF &constraint) const
+{
+  return QSizeF();
+}
+
+QPointF DesktopLayout::positionHorizontally(const QSizeF &itemSize, VertAlign yAlign, HorizAlign xAlign, qreal spL, qreal spR, qreal spT, qreal spB) const
+{
+    QPointF final = QPointF(-1,-1);
+
+    /* space needed for the applet - spacing added */
+    QSizeF size = QSizeF(itemSize.width()+spL+spR, itemSize.height()+spT+spB);
+
+    /* The algorithm works by placing a rectangle of needed size (above) somewhere
+       on the screen and checking for obstacles. x and y hold the top-left
+       coordinates of the rectangle being tested.
+       Start with the rectangle in the requested corner of the screen. */
+    qreal x = 0;
+    qreal y = 0;
+    if (xAlign == Right) {
+        x = effectiveRect.width()-size.width();
+    }
+
+    if (yAlign == Bottom) {
+        y = effectiveRect.height()-size.height();
+    }
+
+    while (1) {
+        bool outOfX, outOfY;
+
+        /* Place items horizontally - first try positions at the same height.
+           Once there are no more positions at the same height to try, try positions
+           on a different height.
+
+           If aligning left, X starts at 0 and increases as we try
+           positions more right. We must skip to a new Y once the
+           remaining X-space is less than the needed width (X + applet width > screen width).
+
+           If aligining right, X starts with the x coordinate of the top-left point
+           of the most-right rectangle and decreases as we try positions more left.
+           Skip to a new Y when there is no more space on the left (X<0). */
+
+        if (xAlign == Left) {
+            outOfX = (x + size.width() > effectiveRect.width());
+        } else {
+            outOfX = (x < 0);
+        }
+
+        /* Every time we run out of X space, we try positions at a new hight.
+
+           If aligning to top, Y starts at 0 and increases as lower positions are tried.
+           If aligning to bottom, Y starts at the y coordinate of a most-bottom rectangle
+           and decreases as higher positions are tried.
+           If there is no more Y space, all possible positions have been tried,
+           and there is no place for the applet.
+        */
+
+        if (yAlign == Bottom) {
+            outOfY = (y < 0);
+        } else {
+            outOfY = (y + size.height() > effectiveRect.height());
+        }
+
+        if (outOfY) {
+            break;
+        }
+
+        if (outOfX) {
+            /* Jumping to the next height
+
+               Take a rectangle ("strap") that expands horizontally over the whole screen,
+               and vertically starts and ends with the same lines as the previously
+               tested rectangles.
+
+               The next height at which the applet could possibly be placed is obtained as follows:
+               - if aligning to top, find the obstacle intersecting the strap that vertically
+                 ends at the highest position, and take the height where it ends,
+               - if aligning to bottom, find the obstacle intersecting the strap that vertically
+                 starts at the lowest position, and take the height where is starts.
+            */
+
+            QRectF a;
+            if (yAlign == Top) {
+                a = itemInRegionEndingFirstVert(QRectF(0, y, effectiveRect.width(), size.height()));
+            } else {
+                a = itemInRegionStartingLastVert(QRectF(0, y, effectiveRect.width(), size.height()));
+            }
+
+            /* If there were no obstacles, the whole screen is too narrow */
+            if (!a.isValid()) {
+                break;
+            }
+
+            /* Jump to the new height */
+            if (yAlign == Top) {
+                y = a.y() + a.height();
+            } else {
+                y = a.y() - size.height();
+            }
+
+            /* Set the starting X position for the new height */
+            if (xAlign == Left) {
+                x = 0;
+            } else {
+                x = effectiveRect.width()-size.width();
+            }
+        } else {
+            /* Check for obstacles in out rectangle and possibly try the next position
+               on this height.
+               If there are obstacles, the next X position
+               where the applet could possibly be placed is
+               obtained by:
+               - if aligning to left, find the obstacle intersecting with our rectangle that
+                 horizontally ends the farthest on the right
+               - if aligining to right, find the obstacle intersecting with our rectangle
+                 that horizontally starts the farthest on the left */
+
+            QRectF a;
+            if (xAlign == Left) {
+                a = itemInRegionEndingLastHoriz(QRectF(x, y, size.width(), size.height()));
+            } else {
+                a = itemInRegionStartingFirstHoriz(QRectF(x, y, size.width(), size.height()));
+            }
+
+            /* no obstacle, place the applet! */
+            if (!a.isValid()) {
+                final = QPointF(x+spL, y+spT);
+                break;
+            }
+
+            /* try next position */
+            if (xAlign == Left) {
+                x = a.x() + a.width();
+            } else {
+                x = a.x() - size.width();
+            }
+        }
+    }
+    return final;
+}
+
+/* Instead of placing horizontally, place vertically.
+   So, try different heights and possibly jump to the next X */
+QPointF DesktopLayout::positionVertically(const QSizeF &itemSize, VertAlign yAlign, HorizAlign xAlign, qreal spL, qreal spR, qreal spT, qreal spB) const
+{
+    /* all the same here */
+
+    QPointF final = QPointF(-1,-1);
+
+    QSizeF size = QSizeF(itemSize.width()+spL+spR, itemSize.height()+spT+spB);
+
+    qreal x = 0;
+    qreal y = 0;
+    if (xAlign == Right)  x = effectiveRect.width()-size.width();
+    if (yAlign == Bottom) y = effectiveRect.height()-size.height();
+
+    while (1) {
+        bool outOfX, outOfY;
+
+        /* These are unchanged */
+
+        if (xAlign == Left) {
+            outOfX = (x + size.width() > effectiveRect.width());
+        } else {
+            outOfX = (x < 0);
+        }
+
+        if (yAlign == Bottom) {
+            outOfY = (y < 0);
+        } else {
+            outOfY = (y + size.height() > effectiveRect.height());
+        }
+
+        /* instead of stopping when all heights have been tried
+           stop when all X positions have been tried */
+        if (outOfX) {
+            break;
+        }
+
+        if (outOfY) {
+            /* Jumping to the next X position
+               Take a vertical strap. */
+
+            QRectF a;
+            if (xAlign == Left) {
+                a = itemInRegionEndingFirstHoriz(QRectF(x, 0, size.width(), effectiveRect.height()));
+            } else {
+                a = itemInRegionStartingLastHoriz(QRectF(x, 0, size.width(), effectiveRect.height()));
+            }
+
+            /* Screen is not high enough */
+            if (!a.isValid()) {
+                break;
+            }
+
+            /* Jump to the new X */
+            if (xAlign == Left) {
+                x = a.x() + a.width();
+            } else {
+                x = a.x() - size.width();
+            }
+
+            /* Set the starting height position for the new X */
+            if (yAlign == Top) {
+                y = 0;
+            } else {
+                y = effectiveRect.height()-size.height();
+            }
+        } else {
+            /* Instead look for obstacles vertically */
+
+            QRectF a;
+            if (yAlign == Top) {
+                a = itemInRegionEndingLastVert(QRectF(x, y, size.width(), size.height()));
+            } else {
+                a = itemInRegionStartingFirstVert(QRectF(x, y, size.width(), size.height()));
+            }
+
+            /* no obstacle, place the applet! */
+            if (!a.isValid()) {
+                final = QPointF(x+spL, y+spT);
+                break;
+            }
+
+            /* try next position */
+            if (yAlign == Top) {
+                y = a.y() + a.height();
+            } else {
+                y = a.y() - size.height();
+            }
+        }
+    }
+    return final;
+}
+
+QRectF DesktopLayout::itemInRegionStartingFirstHoriz(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = std::numeric_limits<qreal>::max();
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().x();
+      if (item.item->geometry().intersects(region) && cl < l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
+
+QRectF DesktopLayout::itemInRegionEndingLastHoriz(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = -1;
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().x() + item.item->geometry().width();
+      if (item.item->geometry().intersects(region) && cl > l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
+
+QRectF DesktopLayout::itemInRegionEndingFirstVert(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = std::numeric_limits<qreal>::max();
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().y() + item.item->geometry().height();
+      if (item.item->geometry().intersects(region) && cl < l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
+
+QRectF DesktopLayout::itemInRegionStartingLastVert(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = -1;
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().y();
+      if (item.item->geometry().intersects(region) && cl > l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
+
+QRectF DesktopLayout::itemInRegionStartingFirstVert(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = std::numeric_limits<qreal>::max();
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().y();
+      if (item.item->geometry().intersects(region) && cl < l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
+
+QRectF DesktopLayout::itemInRegionEndingLastVert(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = -1;
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().y() + item.item->geometry().height();
+      if (item.item->geometry().intersects(region) && cl > l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
+
+QRectF DesktopLayout::itemInRegionEndingFirstHoriz(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = std::numeric_limits<qreal>::max();
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().x() + item.item->geometry().width();
+      if (item.item->geometry().intersects(region) && cl < l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
+
+QRectF DesktopLayout::itemInRegionStartingLastHoriz(const QRectF &region) const
+{
+    QRectF ret = QRectF(0,0,-1,-1);
+    qreal l = -1;
+    for (int i=0; i<items.count(); i++) {
+      DesktopLayoutItem item = items[i];
+      if (!item.lastGeometry.isValid()) {
+        continue;
+      }
+      qreal cl = item.item->geometry().x();
+      if (item.item->geometry().intersects(region) && cl > l) {
+          ret = item.item->geometry();
+          l = cl;
+      }
+    }
+    return ret;
+}
Index: workspace/plasma/containments/desktop/desktop.cpp
===================================================================
--- workspace/plasma/containments/desktop/desktop.cpp	(revision 842464)
+++ workspace/plasma/containments/desktop/desktop.cpp	(working copy)
@@ -79,6 +79,9 @@
     connect(this, SIGNAL(appletAdded(Plasma::Applet *, const QPointF &)),
             this, SLOT(onAppletAdded(Plasma::Applet *, const QPointF &)));
 
+    m_layout = new DesktopLayout;
+    setLayout(m_layout);
+
     //kDebug() << "!!! loading desktop";
 }
 
@@ -438,403 +441,30 @@
 
 void DefaultDesktop::onAppletAdded(Plasma::Applet *applet, const QPointF &pos)
 {
-    if (pos == QPointF(-1, -1)) {
-        QPointF newpos = positionVertically(applet, Top, Left, APPLET_SPACING, APPLET_SPACING, APPLET_SPACING, APPLET_SPACING);
-        if (newpos != QPointF(-1,-1)) {
-            applet->setPos(newpos);
-        }
+    if (pos != QPointF(-1, -1)) {
+        m_layout->addItem(applet, QRectF(pos, applet->size()));
+    } else {
+        m_layout->addItem(applet);
     }
+    connect(applet, SIGNAL(destroyed(QObject *)), this, SLOT(onAppletDestroyed(QObject *)));
+    connect(applet, SIGNAL(geometryChanged()), this, SLOT(onAppletGeometryChanged()));
 }
 
-QPointF DefaultDesktop::positionHorizontally(Applet *applet, VertAlign yAlign, HorizAlign xAlign, qreal spL, qreal spR, qreal spT, qreal spB) const
+void DefaultDesktop::onAppletDestroyed(QObject *applet)
 {
-    QPointF final = QPointF(-1,-1);
-
-    QRectF screenGeom = geometry();
-    if (screen() != -1) {
-        // we are associated with a screen, make sure not to overlap panels
-        QDesktopWidget *desktop = qApp->desktop();
-        screenGeom = desktop->availableGeometry(screen());
-    }
-
-    /* space needed for the applet - spacing added */
-    QSizeF size = QSizeF(applet->geometry().width()+spL+spR, applet->geometry().height()+spT+spB);
-
-    /* The algorithm works by placing a rectangle of needed size (above) somewhere
-       on the screen and checking for obstacles. x and y hold the top-left
-       coordinates of the rectangle being tested.
-       Start with the rectangle in the requested corner of the screen. */
-    qreal x = 0;
-    qreal y = 0;
-    if (xAlign == Right) {
-        x = screenGeom.width()-size.width();
-    }
-
-    if (yAlign == Bottom) {
-        y = screenGeom.height()-size.height();
-    }
-
-    while (1) {
-        bool outOfX, outOfY;
-
-        /* Place items horizontally - first try positions at the same height.
-           Once there are no more positions at the same height to try, try positions
-           on a different height.
-
-           If aligning left, X starts at 0 and increases as we try
-           positions more right. We must skip to a new Y once the
-           remaining X-space is less than the needed width (X + applet width > screen width).
-
-           If aligining right, X starts with the x coordinate of the top-left point
-           of the most-right rectangle and decreases as we try positions more left.
-           Skip to a new Y when there is no more space on the left (X<0). */
-
-        if (xAlign == Left) {
-            outOfX = (x + size.width() > screenGeom.width());
-        } else {
-            outOfX = (x < 0);
+    for (int i=0; i < m_layout->count(); i++) {
+        if ((Applet *)applet == m_layout->itemAt(i)) {
+            m_layout->removeAt(i);
+            return;
         }
-
-        /* Every time we run out of X space, we try positions at a new hight.
-
-           If aligning to top, Y starts at 0 and increases as lower positions are tried.
-           If aligning to bottom, Y starts at the y coordinate of a most-bottom rectangle
-           and decreases as higher positions are tried.
-           If there is no more Y space, all possible positions have been tried,
-           and there is no place for the applet.
-        */
-
-        if (yAlign == Bottom) {
-            outOfY = (y < 0);
-        } else {
-            outOfY = (y + size.height() > screenGeom.height());
-        }
-
-        if (outOfY) {
-            break;
-        }
-
-        if (outOfX) {
-            /* Jumping to the next height
-
-               Take a rectangle ("strap") that expands horizontally over the whole screen,
-               and vertically starts and ends with the same lines as the previously
-               tested rectangles.
-
-               The next height at which the applet could possibly be placed is obtained as follows:
-               - if aligning to top, find the obstacle intersecting the strap that vertically
-                 ends at the highest position, and take the height where it ends,
-               - if aligning to bottom, find the obstacle intersecting the strap that vertically
-                 starts at the lowest position, and take the height where is starts.
-            */
-
-            QRectF a;
-            if (yAlign == Top) {
-                a = appletInRegionEndingFirstVert(applet, QRectF(0, y, screenGeom.width(), size.height()));
-            } else {
-                a = appletInRegionStartingLastVert(applet, QRectF(0, y, screenGeom.width(), size.height()));
-            }
-
-            /* If there were no obstacles, the whole screen is too narrow */
-            if (!a.isValid()) {
-                break;
-            }
-
-            /* Jump to the new height */
-            if (yAlign == Top) {
-                y = a.y() + a.height();
-            } else {
-                y = a.y() - size.height();
-            }
-
-            /* Set the starting X position for the new height */
-            if (xAlign == Left) {
-                x = 0;
-            } else {
-                x = screenGeom.width()-size.width();
-            }
-        } else {
-            /* Check for obstacles in out rectangle and possibly try the next position
-               on this height.
-               If there are obstacles, the next X position
-               where the applet could possibly be placed is
-               obtained by:
-               - if aligning to left, find the obstacle intersecting with our rectangle that
-                 horizontally ends the farthest on the right
-               - if aligining to right, find the obstacle intersecting with our rectangle
-                 that horizontally starts the farthest on the left */
-
-            QRectF a;
-            if (xAlign == Left) {
-                a = appletInRegionEndingLastHoriz(applet, QRectF(x, y, size.width(), size.height()));
-            } else {
-                a = appletInRegionStartingFirstHoriz(applet, QRectF(x, y, size.width(), size.height()));
-            }
-
-            /* no obstacle, place the applet! */
-            if (!a.isValid()) {
-                final = QPointF(x+spL, y+spT);
-                break;
-            }
-
-            /* try next position */
-            if (xAlign == Left) {
-                x = a.x() + a.width();
-            } else {
-                x = a.x() - size.width();
-            }
-        }
     }
-    return final;
 }
 
-/* Instead of placing horizontally, place vertically.
-   So, try different heights and possibly jump to the next X */
-QPointF DefaultDesktop::positionVertically(Applet *applet, VertAlign yAlign, HorizAlign xAlign, qreal spL, qreal spR, qreal spT, qreal spB) const
+void DefaultDesktop::onAppletGeometryChanged()
 {
-    /* all the same here */
-
-    QRectF screenGeom = geometry();
-    if (screen() != -1) {
-        QDesktopWidget *desktop = qApp->desktop();
-        screenGeom = desktop->availableGeometry(screen());
-    }
-
-    QPointF final = QPointF(-1,-1);
-
-    QSizeF size = QSizeF(applet->geometry().width()+spL+spR, applet->geometry().height()+spT+spB);
-
-    qreal x = 0;
-    qreal y = 0;
-    if (xAlign == Right)  x = screenGeom.width()-size.width();
-    if (yAlign == Bottom) y = screenGeom.height()-size.height();
-
-    while (1) {
-        kDebug() << "----- TRY" << x << y;
-        bool outOfX, outOfY;
-
-        /* These are unchanged */
-
-        if (xAlign == Left) {
-            outOfX = (x + size.width() > screenGeom.width());
-        } else {
-            outOfX = (x < 0);
-        }
-
-        if (yAlign == Bottom) {
-            outOfY = (y < 0);
-        } else {
-            outOfY = (y + size.height() > screenGeom.height());
-        }
-
-        /* instead of stopping when all heights have been tried
-           stop when all X positions have been tried */
-        if (outOfX) {
-            break;
-        }
-
-        if (outOfY) {
-            /* Jumping to the next X position
-               Take a vertical strap. */
-
-            QRectF a;
-            if (xAlign == Left) {
-                a = appletInRegionEndingFirstHoriz(applet, QRectF(x, 0, size.width(), screenGeom.height()));
-            } else {
-                a = appletInRegionStartingLastHoriz(applet, QRectF(x, 0, size.width(), screenGeom.height()));
-            }
-
-            /* Screen is not high enough */
-            if (!a.isValid()) {
-                break;
-            }
-
-            /* Jump to the new X */
-            if (xAlign == Left) {
-                x = a.x() + a.width();
-            } else {
-                x = a.x() - size.width();
-            }
-
-            /* Set the starting height position for the new X */
-            if (yAlign == Top) {
-                y = 0;
-            } else {
-                y = screenGeom.height()-size.height();
-            }
-        } else {
-            /* Instead look for obstacles vertically */
-
-            QRectF a;
-            if (yAlign == Top) {
-                a = appletInRegionEndingLastVert(applet, QRectF(x, y, size.width(), size.height()));
-            } else {
-                a = appletInRegionStartingFirstVert(applet, QRectF(x, y, size.width(), size.height()));
-            }
-
-            /* no obstacle, place the applet! */
-            if (!a.isValid()) {
-                final = QPointF(x+spL, y+spT);
-                break;
-            }
-
-            /* try next position */
-            if (yAlign == Top) {
-                y = a.y() + a.height();
-            } else {
-                y = a.y() - size.height();
-            }
-        }
-    }
-    return final;
+    m_layout->itemGeometryChanged((Applet *)sender());
 }
 
-QRectF DefaultDesktop::appletInRegionStartingFirstHoriz(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = std::numeric_limits<qreal>::max();
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().x();
-        if (applet->geometry().intersects(region) && cl < l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-QRectF DefaultDesktop::appletInRegionEndingLastHoriz(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = -1;
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().x()+applet->geometry().width();
-        if (applet->geometry().intersects(region) && cl > l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-QRectF DefaultDesktop::appletInRegionEndingFirstVert(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = std::numeric_limits<qreal>::max();
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().y()+applet->geometry().height();
-        if (applet->geometry().intersects(region) && cl < l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-QRectF DefaultDesktop::appletInRegionStartingLastVert(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = -1;
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().y();
-        if (applet->geometry().intersects(region) && cl > l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-
-QRectF DefaultDesktop::appletInRegionStartingFirstVert(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = std::numeric_limits<qreal>::max();
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().y();
-        if (applet->geometry().intersects(region) && cl < l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-QRectF DefaultDesktop::appletInRegionEndingLastVert(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = -1;
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().y()+applet->geometry().height();
-        if (applet->geometry().intersects(region) && cl > l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-QRectF DefaultDesktop::appletInRegionEndingFirstHoriz(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = std::numeric_limits<qreal>::max();
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().x()+applet->geometry().width();
-        if (applet->geometry().intersects(region) && cl < l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-QRectF DefaultDesktop::appletInRegionStartingLastHoriz(Plasma::Applet *target, const QRectF &region) const
-{
-    QRectF ret = QRectF(0,0,-1,-1);
-    qreal l = -1;
-    foreach (Applet *applet, applets()) {
-        if (applet == target) {
-            continue;
-        }
-
-        qreal cl = applet->geometry().x();
-        if (applet->geometry().intersects(region) && cl > l) {
-            ret = applet->geometry();
-            l = cl;
-        }
-    }
-    return ret;
-}
-
-
 K_EXPORT_PLASMA_APPLET(desktop, DefaultDesktop)
 
 #include "desktop.moc"
Index: workspace/plasma/containments/desktop/desktoplayout.h
===================================================================
--- workspace/plasma/containments/desktop/desktoplayout.h	(revision 0)
+++ workspace/plasma/containments/desktop/desktoplayout.h	(revision 0)
@@ -0,0 +1,68 @@
+#ifndef _DESKTOPLAYOUT_H
+#define _DESKTOPLAYOUT_H
+
+#include <QGraphicsLayout>
+#include <QList>
+
+class DesktopLayoutItem
+{
+  public:
+    QGraphicsLayoutItem *item;
+    QRectF lastGeometry;
+    QRectF preferredGeometry;
+};
+
+class DesktopLayout : public QGraphicsLayout
+{
+  public:
+    DesktopLayout (QGraphicsLayoutItem *parent = 0);
+    virtual ~DesktopLayout ();
+
+    // add an item with the specified preferred geometry
+    void addItem (QGraphicsLayoutItem *item, QRectF preferredGeometry);
+    // add an item and position it automatically
+    void addItem (QGraphicsLayoutItem *item);
+
+    void setPlacementSpacing (qreal spacing);
+    void setShiftingSpacing (qreal spacing);
+
+    // call this when an item has been manually moved/resized
+    void itemGeometryChanged (QGraphicsLayoutItem *layoutItem);
+
+    // inherited from QGraphicsLayout
+    int count() const;
+    QGraphicsLayoutItem *itemAt(int index) const;
+    void removeAt(int index);
+
+    // inherited from QGraphicsLayoutItem
+    void setGeometry(const QRectF &rect);
+    QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+  private:
+    QList<DesktopLayoutItem> items;
+    QRectF effectiveRect;
+
+    static const qreal defaultPlacementSpacing = 20;
+    static const qreal defaultShiftingSpacing = 15;
+    qreal placementSpacing;
+    qreal shiftingSpacing;
+
+    QRectF limitPosition(const QRectF &preferred) const;;
+
+    enum HorizAlign { Left = 0, Right = 1 };
+    enum VertAlign { Top = 0, Bottom = 1 };
+
+    QPointF positionHorizontally(const QSizeF &itemSize, VertAlign yAlign, HorizAlign xAlign, qreal spL, qreal spR, qreal spT, qreal spB) const;
+    QPointF positionVertically(const QSizeF &itemSize, VertAlign yAlign, HorizAlign xAlign, qreal spL, qreal spR, qreal spT, qreal spB) const;
+
+    QRectF itemInRegionStartingFirstHoriz(const QRectF &region) const;
+    QRectF itemInRegionEndingLastHoriz(const QRectF &region) const;
+    QRectF itemInRegionEndingFirstVert(const QRectF &region) const;
+    QRectF itemInRegionStartingLastVert(const QRectF &region) const;
+    QRectF itemInRegionStartingFirstVert(const QRectF &region) const;
+    QRectF itemInRegionEndingLastVert(const QRectF &region) const;
+    QRectF itemInRegionEndingFirstHoriz(const QRectF &region) const;
+    QRectF itemInRegionStartingLastHoriz(const QRectF &region) const;
+};
+
+#endif
Index: workspace/plasma/containments/desktop/desktop.h
===================================================================
--- workspace/plasma/containments/desktop/desktop.h	(revision 842464)
+++ workspace/plasma/containments/desktop/desktop.h	(working copy)
@@ -34,6 +34,7 @@
 #include <plasma/animator.h>
 
 #include "renderthread.h"
+#include "desktoplayout.h"
 
 class BackgroundDialog;
 class QAction;
@@ -96,6 +97,8 @@
     void addPanel();
 
     void onAppletAdded(Plasma::Applet *, const QPointF &);
+    void onAppletDestroyed(QObject *applet);
+    void onAppletGeometryChanged();
 
 private:
     void reloadConfig();
@@ -152,6 +155,8 @@
 
     RenderThread m_renderer;
     int m_rendererToken;
+
+    DesktopLayout *m_layout;
 };
 
 #endif // PLASMA_PANEL_H
Index: workspace/plasma/containments/desktop/CMakeLists.txt
===================================================================
--- workspace/plasma/containments/desktop/CMakeLists.txt	(revision 842464)
+++ workspace/plasma/containments/desktop/CMakeLists.txt	(working copy)
@@ -2,7 +2,8 @@
     desktop.cpp
     backgrounddialog.cpp
     backgroundpackage.cpp
-    renderthread.cpp)
+    renderthread.cpp
+    desktoplayout.cpp)
 
 kde4_add_ui_files( desktop_SRCS BackgroundDialog.ui )
 
