From a915bf7e6b6522966bc3aa487ad791c48e03c278 Mon Sep 17 00:00:00 2001 From: "Aaron J. Seigo" Date: Fri, 25 Apr 2008 18:44:09 +0000 Subject: [PATCH] * move containmentConstraintsUpdated to the pimpl * move Containment::Private to it's own header (eventually subclass Applet::private?) * have applet use Containment::Private methods to hide gory internal details ;) * Toolbox -> ToolBox svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=801127 --- applet.cpp | 3 +- containment.cpp | 856 +++++++++++++++++++++++------------------------- containment.h | 10 +- containment_p.h | 90 +++++ 4 files changed, 497 insertions(+), 462 deletions(-) create mode 100644 containment_p.h diff --git a/applet.cpp b/applet.cpp index 6015e81d6..084858702 100644 --- a/applet.cpp +++ b/applet.cpp @@ -57,6 +57,7 @@ #include "plasma/configxml.h" #include "plasma/containment.h" +#include "plasma/containment_p.h" #include "plasma/corona.h" #include "plasma/dataenginemanager.h" #include "plasma/package.h" @@ -933,7 +934,7 @@ void Applet::flushUpdatedConstraints() Containment* containment = qobject_cast(this); if (isContainment() && containment) { - containment->containmentConstraintsUpdated(c); + containment->d->containmentConstraintsUpdated(c); } constraintsUpdated(c); diff --git a/containment.cpp b/containment.cpp index 493f94018..38186f4db 100644 --- a/containment.cpp +++ b/containment.cpp @@ -19,6 +19,7 @@ */ #include "containment.h" +#include "containment_p.h" #include #include @@ -51,109 +52,6 @@ namespace Plasma { -static const int INTER_CONTAINMENT_MARGIN = 6; -static const int VERTICAL_STACKING_OFFSET = 10000; - -class Containment::Private -{ -public: - Private(Containment* c) - : q(c), - formFactor(Planar), - location(Floating), - screen(-1), // no screen - toolbox(0), - type(Containment::NoContainmentType), - positioning(false) - { - } - - ~Private() - { - qDeleteAll(applets); - applets.clear(); - } - - Toolbox* createToolbox() - { - if (!toolbox) { - switch (type) { - case PanelContainment: - toolbox = new PanelToolbox(q); - break; - //defaults to DesktopContainment right now - default: - toolbox = new DesktopToolbox(q); - break; - } - positionToolbox(); - } - - return toolbox; - } - - void positionToolbox() - { - QRectF r; - if (screen < 0) { - r = q->geometry(); - } else { - QDesktopWidget *desktop = QApplication::desktop(); - r = desktop->availableGeometry(screen); - } - - toolbox->setPos(QPointF(r.right(), r.y())); - } - - void triggerShowAddWidgets() - { - emit q->showAddWidgetsInterface(QPointF()); - } - - bool regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet=0) const; - void positionPanel(bool force = false); - void positionContainment(); - void setLockToolText(); - void handleDisappeared(AppletHandle *handle); - void destroyApplet(); - /** - * Repositions the Plasma toolbox. Useful to ensure its always in the correct place within the view. - */ - void repositionToolbox(); - void appletDestroyed(QObject*); - void appletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim); - - Applet* addApplet(const QString& name, const QVariantList& args = QVariantList(), - const QRectF &geometry = QRectF(-1, -1, -1, -1), uint id = 0, - bool delayedInit = false); - - Containment *q; - FormFactor formFactor; - Location location; - Applet::List applets; - QMap handles; - int screen; - Toolbox *toolbox; - Containment::Type type; - bool positioning; -}; - -void Containment::Private::setLockToolText() -{ - if (toolbox) { - Icon *icon = dynamic_cast(toolbox->tool("lockWidgets")); - if (icon) { - // we know it's an icon becase we made it - icon->setText(q->immutability() != NotImmutable ? i18n("Unlock Widgets") : - i18n("Lock Widgets")); - QSizeF iconSize = icon->sizeFromIconSize(22); - icon->setMinimumSize(iconSize); - icon->setMaximumSize(iconSize); - icon->resize(icon->size()); - } - } -} - Containment::StyleOption::StyleOption() : QStyleOptionGraphicsItem(), view(0) @@ -289,61 +187,6 @@ void Containment::saveContainment(KConfigGroup* group) const group->writeEntry("location", (int)d->location); } -void Containment::containmentConstraintsUpdated(Plasma::Constraints constraints) -{ - if (!isContainment()) { - return; - } - - //kDebug() << "got containmentConstraintsUpdated" << constraints << (QObject*)d->toolbox; - if (constraints & Plasma::ImmutableConstraint) { - d->setLockToolText(); - - // tell the applets too - foreach (Applet *a, d->applets) { - a->constraintsUpdated(ImmutableConstraint); - } - } - - if ((constraints & Plasma::SizeConstraint || constraints & Plasma::ScreenConstraint) && - d->toolbox) { - //The placement assumes that the geometry width/height is no more than the screen - if (d->type == PanelContainment) { - if (formFactor() == Vertical) { - d->toolbox->setOrientation(Qt::Vertical); - d->toolbox->setPos(geometry().width()/2 - d->toolbox->boundingRect().width()/2, geometry().height()); - //defaulting to Horizontal right now - } else { - d->toolbox->setOrientation(Qt::Horizontal); - d->toolbox->setPos(geometry().width(), geometry().height()/2 - d->toolbox->boundingRect().height()/2); - } - } else { - d->toolbox->setPos(geometry().right() - qAbs(d->toolbox->boundingRect().width()), 0); - } - d->toolbox->enableTool("addwidgets", immutability() == NotImmutable); - } - - if (constraints & Plasma::FormFactorConstraint && d->toolbox) { - if (formFactor() == Vertical) { - d->toolbox->setOrientation(Qt::Vertical); - //defaults to horizontal - } else { - d->toolbox->setOrientation(Qt::Horizontal); - } - } - - if (constraints & Plasma::SizeConstraint) { - switch (containmentType()) { - case PanelContainment: - d->positionPanel(); - break; - default: - d->positionContainment(); - break; - } - } -} - Containment::Type Containment::containmentType() const { return d->type; @@ -354,7 +197,7 @@ void Containment::setContainmentType(Containment::Type type) d->type = type; if (isContainment() && type == DesktopContainment) { - if (!d->toolbox) { + if (!d->toolBox) { QGraphicsWidget *addWidgetTool = addToolBoxTool("addwidgets", "list-add", i18n("Add Widgets")); connect(addWidgetTool, SIGNAL(clicked()), this, SLOT(triggerShowAddWidgets())); @@ -376,14 +219,14 @@ void Containment::setContainmentType(Containment::Type type) } } else if (isContainment() && type == PanelContainment) { - if (!d->toolbox) { - d->createToolbox(); - d->toolbox->setSize(22); - d->toolbox->setIconSize(QSize(16, 16)); + if (!d->toolBox) { + d->createToolBox(); + d->toolBox->setSize(22); + d->toolBox->setIconSize(QSize(16, 16)); } } else { - delete d->toolbox; - d->toolbox = 0; + delete d->toolBox; + d->toolBox = 0; } } @@ -504,18 +347,6 @@ void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) desktopMenu.exec(event->screenPos()); } -void Containment::Private::destroyApplet() -{ - QAction *action = qobject_cast(q->sender()); - - if (!action) { - return; - } - - Applet *applet = qobject_cast(action->data().value()); - Animator::self()->animateItem(applet, Animator::DisappearAnimation); -} - void Containment::setFormFactor(FormFactor formFactor) { if (d->formFactor == formFactor && layout()) { @@ -688,37 +519,6 @@ Applet* Containment::addApplet(const QString& name, const QVariantList& args, co return d->addApplet(name, args, appletGeometry); } -Applet* Containment::Private::addApplet(const QString& name, const QVariantList& args, - const QRectF& appletGeometry, uint id, bool delayInit) -{ - if (!delayInit && q->immutability() != NotImmutable) { - kDebug() << "addApplet for" << name << "requested, but we're currently immutable!"; - return 0; - } - - QGraphicsView *v = q->view(); - if (v) { - v->setCursor(Qt::BusyCursor); - } - - Applet* applet = Applet::load(name, id, args); - if (v) { - v->unsetCursor(); - } - - if (!applet) { - kDebug() << "Applet" << name << "could not be loaded."; - applet = new Applet; - } - - //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry(); - - connect(applet, SIGNAL(configNeedsSaving()), q, SIGNAL(configNeedsSaving())); - connect(applet, SIGNAL(launchActivated()), q, SIGNAL(launchActivated())); - q->addApplet(applet, appletGeometry.topLeft(), delayInit); - return applet; -} - //pos must be relative to the containment already. use mapfromscene. //what we're trying to do here for panels is make the applet go to the requested position, //or somewhere close to it, and get integrated properly into the containment as if it were created @@ -781,55 +581,6 @@ void Containment::addApplet(Applet *applet, const QPointF &pos, bool delayInit) emit appletAdded(applet, pos); } -bool Containment::Private::regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet) const -{ - foreach (Applet *applet, applets) { - if (applet != ignoredApplet && applet->geometry().intersects(region)) { - return false; - } - } - return true; -} - -void Containment::Private::appletDestroyed(QObject* object) -{ - // we do a static_cast here since it really isn't an Applet by this - // point anymore since we are in the qobject dtor. we don't actually - // try and do anything with it, we just need the value of the pointer - // so this unsafe looking code is actually just fine. - Applet* applet = static_cast(object); - applets.removeAll(applet); - emit q->appletRemoved(applet); - emit q->configNeedsSaving(); -} - -void Containment::Private::appletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim) -{ - if (anim == Animator::DisappearAnimation) { - QGraphicsItem *parent = item->parentItem(); - - while (parent) { - if (parent == q) { - Applet *applet = qgraphicsitem_cast(item); - - if (applet) { - applet->destroy(); - } - - break; - } - - parent = parent->parentItem(); - } - } else if (anim == Animator::AppearAnimation) { - if (q->containmentType() == DesktopContainment && - item->parentItem() == q && - qgraphicsitem_cast(item)) { - item->installSceneEventFilter(q); - } - } -} - Applet::List Containment::applets() const { return d->applets; @@ -842,9 +593,9 @@ void Containment::setScreen(int screen) #ifndef Q_OS_WIN // we want to listen to changes in work area if our screen changes if (d->screen < 0 && screen > -1) { - connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(repositionToolbox())); + connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(positionToolbox())); } else if (screen < 0) { - disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(repositionToolbox())); + disconnect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(positionToolbox())); } #endif if (screen > -1 && corona()) { @@ -880,180 +631,6 @@ void Containment::setScreen(int screen) } } -void Containment::Private::positionContainment() -{ - Corona *c = q->corona(); - if (!c) { - return; - } - - QList containments = c->containments(); - QMutableListIterator it(containments); - - while (it.hasNext()) { - Containment *containment = it.next(); - if (containment == q || - containment->containmentType() == PanelContainment) { - // weed out all containments we don't care about at all - // e.g. Panels and ourself - it.remove(); - continue; - } - - if (q->collidesWithItem(containment)) { - break; - } - } - - if (!it.hasNext()) { - // we made it all the way through the list, we have no - // collisions - return; - } - - // we need to find how many screens are to our top and left - // to calculate the proper offsets for the margins. - int width = 0; - int height = 0; - - QDesktopWidget *desktop = QApplication::desktop(); - int numScreens = desktop->numScreens(); - - for (int i = 0; i < numScreens; ++i) { - QRect otherScreen = desktop->screenGeometry(i); - - if (width < otherScreen.width()) { - width = otherScreen.width(); - } - - if (height < otherScreen.height()) { - height = otherScreen.height(); - } - } - - //this magic number (4) is the number of columns to try before going to the next row - width = (width + INTER_CONTAINMENT_MARGIN) * 4; - height += INTER_CONTAINMENT_MARGIN; - - // a mildly naive "find the first slot" approach - QRectF r = q->boundingRect(); - QPointF topLeft(0, 0); - q->setPos(topLeft); - - positioning = true; - while (true) { - it.toFront(); - - while (it.hasNext()) { - Containment *containment = it.next(); - if (q->collidesWithItem(containment)) { - break; - } - - QPointF pos = containment->pos(); - if (pos.x() <= topLeft.x() && pos.y() <= topLeft.y()) { - // we don't colid with this containment, and it's above - // and to the left of us, so let's not bother checking - // it again if we go through this loop again - it.remove(); - } - } - - if (!it.hasNext()) { - // success! no collisions! - break; - } - - if (topLeft.x() + (r.width() * 2) + INTER_CONTAINMENT_MARGIN > width) { - // we ran out of width room, try another row - topLeft = QPoint(0, topLeft.y() + height); - } else { - topLeft.setX(topLeft.x() + r.width() + INTER_CONTAINMENT_MARGIN); - } - - kDebug() << "trying at" << topLeft; - q->setPos(topLeft); - //kDebug() << collidingItems().count() << collidingItems()[0] << (QGraphicsItem*)this; - } - - positioning = false; -} - -void Containment::Private::positionPanel(bool force) -{ - if (!q->scene()) { - kDebug() << "no scene yet"; - return; - } - - // we position panels in negative coordinates, and stack all horizontal - // and all vertical panels with each other. - - const QPointF p = q->pos(); - - if (!force && - p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN && - q->scene()->collidingItems(q).isEmpty()) { - // already positioned and not running into any other panels - return; - } - - //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here - bool horiz = q->formFactor() == Plasma::Horizontal; - qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET; - qreal lastHeight = 0; - - // this should be ok for small numbers of panels, but we ever end - // up managing hundreds of them, this simplistic alogrithm will - // likely be too slow. - foreach (const Containment* other, q->corona()->containments()) { - if (other == q || - other->containmentType() != PanelContainment || - horiz != (other->formFactor() == Plasma::Horizontal)) { - // only line up with panels of the same orientation - continue; - } - - if (horiz) { - qreal y = other->pos().y(); - if (y < bottom) { - lastHeight = other->size().height(); - bottom = y; - } - } else { - qreal width = other->size().width(); - qreal x = other->pos().x() + width; - if (x > bottom) { - lastHeight = width; - bottom = x + lastHeight; - } - } - } - - kDebug() << "positioning" << (horiz ? "" : "non-") << "horizontal panel; forced?" << force; - // give a space equal to the height again of the last item so there is - // room to grow. - QPointF newPos; - if (horiz) { - bottom -= lastHeight + INTER_CONTAINMENT_MARGIN; - //TODO: fix x position for non-flush-left panels - kDebug() << "moved to" << QPointF(0, bottom - q->size().height()); - newPos = QPointF(0, bottom - q->size().height()); - } else { - bottom += lastHeight + INTER_CONTAINMENT_MARGIN; - //TODO: fix y position for non-flush-top panels - kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); - newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); - } - - positioning = true; - if (p != newPos) { - q->setPos(newPos); - emit q->geometryChanged(); - } - positioning = false; -} - int Containment::screen() const { return d->screen; @@ -1243,12 +820,6 @@ bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event) return false; } -void Containment::Private::handleDisappeared(AppletHandle *handle) -{ - handles.remove(handle->applet()); - handle->deleteLater(); -} - QVariant Containment::itemChange(GraphicsItemChange change, const QVariant &value) { Q_UNUSED(value) @@ -1282,48 +853,429 @@ QGraphicsWidget * Containment::addToolBoxTool(const QString& toolName, const QSt tool->setMaximumSize(iconSize); tool->resize(tool->size()); - d->createToolbox()->addTool(tool, toolName); + d->createToolBox()->addTool(tool, toolName); return tool; } void Containment::enableToolBoxTool(const QString &toolname, bool enable) { - if (d->toolbox) { - d->toolbox->enableTool(toolname, enable); + if (d->toolBox) { + d->toolBox->enableTool(toolname, enable); } } bool Containment::isToolboxToolEnabled(const QString &toolname) const { - if (d->toolbox) { - return d->toolbox->isToolEnabled(toolname); + if (d->toolBox) { + return d->toolBox->isToolEnabled(toolname); } return false; } void Containment::showToolbox() { - if (d->toolbox) { - d->toolbox->showToolbox(); + if (d->toolBox) { + d->toolBox->showToolbox(); } } void Containment::hideToolbox() { - if (d->toolbox) { - d->toolbox->hideToolbox(); + if (d->toolBox) { + d->toolBox->hideToolbox(); } } -void Containment::Private::repositionToolbox() + +// Private class implementation + +Toolbox* Containment::Private::createToolBox() { - //kDebug() << "reposition" << d->screen << (QObject*)d->toolbox; - if (toolbox) { - positionToolbox(); + if (!toolBox) { + switch (type) { + case PanelContainment: + toolBox = new PanelToolbox(q); + break; + //defaults to DesktopContainment right now + default: + toolBox = new DesktopToolbox(q); + break; + } + positionToolBox(); + } + + return toolBox; +} + +void Containment::Private::positionToolBox() +{ + if (!toolBox) { + return; + } + + QRectF r; + if (screen < 0) { + r = q->geometry(); + } else { + QDesktopWidget *desktop = QApplication::desktop(); + r = desktop->availableGeometry(screen); + } + + toolBox->setPos(QPointF(r.right(), r.y())); +} + +void Containment::Private::triggerShowAddWidgets() +{ + emit q->showAddWidgetsInterface(QPointF()); +} + +void Containment::Private::handleDisappeared(AppletHandle *handle) +{ + handles.remove(handle->applet()); + handle->deleteLater(); +} + +void Containment::Private::setLockToolText() +{ + if (!toolBox) { + return; + } + + Icon *icon = dynamic_cast(toolBox->tool("lockWidgets")); + if (icon) { + // we know it's an icon becase we made it + icon->setText(q->immutability() != NotImmutable ? i18n("Unlock Widgets") : + i18n("Lock Widgets")); + QSizeF iconSize = icon->sizeFromIconSize(22); + icon->setMinimumSize(iconSize); + icon->setMaximumSize(iconSize); + icon->resize(icon->size()); } } +void Containment::Private::containmentConstraintsUpdated(Plasma::Constraints constraints) +{ + if (!q->isContainment()) { + return; + } + + //kDebug() << "got containmentConstraintsUpdated" << constraints << (QObject*)toolBox; + if (constraints & Plasma::ImmutableConstraint) { + setLockToolText(); + + // tell the applets too + foreach (Applet *a, applets) { + a->constraintsUpdated(ImmutableConstraint); + } + } + + if ((constraints & Plasma::SizeConstraint || constraints & Plasma::ScreenConstraint) && + toolBox) { + //The placement assumes that the geometry width/height is no more than the screen + if (type == PanelContainment) { + if (q->formFactor() == Vertical) { + toolBox->setOrientation(Qt::Vertical); + toolBox->setPos(q->geometry().width()/2 - toolBox->boundingRect().width()/2, q->geometry().height()); + //defaulting to Horizontal right now + } else { + toolBox->setOrientation(Qt::Horizontal); + toolBox->setPos(q->geometry().width(), q->geometry().height()/2 - toolBox->boundingRect().height()/2); + } + } else { + toolBox->setPos(q->geometry().right() - qAbs(toolBox->boundingRect().width()), 0); + } + toolBox->enableTool("addwidgets", q->immutability() == NotImmutable); + } + + if (constraints & Plasma::FormFactorConstraint && toolBox) { + if (q->formFactor() == Vertical) { + toolBox->setOrientation(Qt::Vertical); + //defaults to horizontal + } else { + toolBox->setOrientation(Qt::Horizontal); + } + } + + if (constraints & Plasma::SizeConstraint) { + switch (q->containmentType()) { + case PanelContainment: + positionPanel(); + break; + default: + positionContainment(); + break; + } + } +} + +void Containment::Private::destroyApplet() +{ + QAction *action = qobject_cast(q->sender()); + + if (!action) { + return; + } + + Applet *applet = qobject_cast(action->data().value()); + Animator::self()->animateItem(applet, Animator::DisappearAnimation); +} + +Applet* Containment::Private::addApplet(const QString& name, const QVariantList& args, + const QRectF& appletGeometry, uint id, bool delayInit) +{ + if (!delayInit && q->immutability() != NotImmutable) { + kDebug() << "addApplet for" << name << "requested, but we're currently immutable!"; + return 0; + } + + QGraphicsView *v = q->view(); + if (v) { + v->setCursor(Qt::BusyCursor); + } + + Applet* applet = Applet::load(name, id, args); + if (v) { + v->unsetCursor(); + } + + if (!applet) { + kDebug() << "Applet" << name << "could not be loaded."; + applet = new Applet; + } + + //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry(); + + connect(applet, SIGNAL(configNeedsSaving()), q, SIGNAL(configNeedsSaving())); + connect(applet, SIGNAL(launchActivated()), q, SIGNAL(launchActivated())); + q->addApplet(applet, appletGeometry.topLeft(), delayInit); + return applet; +} + +bool Containment::Private::regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet) const +{ + foreach (Applet *applet, applets) { + if (applet != ignoredApplet && applet->geometry().intersects(region)) { + return false; + } + } + return true; +} + +void Containment::Private::appletDestroyed(QObject* object) +{ + // we do a static_cast here since it really isn't an Applet by this + // point anymore since we are in the qobject dtor. we don't actually + // try and do anything with it, we just need the value of the pointer + // so this unsafe looking code is actually just fine. + Applet* applet = static_cast(object); + applets.removeAll(applet); + emit q->appletRemoved(applet); + emit q->configNeedsSaving(); +} + +void Containment::Private::appletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim) +{ + if (anim == Animator::DisappearAnimation) { + QGraphicsItem *parent = item->parentItem(); + + while (parent) { + if (parent == q) { + Applet *applet = qgraphicsitem_cast(item); + + if (applet) { + applet->destroy(); + } + + break; + } + + parent = parent->parentItem(); + } + } else if (anim == Animator::AppearAnimation) { + if (q->containmentType() == DesktopContainment && + item->parentItem() == q && + qgraphicsitem_cast(item)) { + item->installSceneEventFilter(q); + } + } +} + +void Containment::Private::positionContainment() +{ + Corona *c = q->corona(); + if (!c) { + return; + } + + QList containments = c->containments(); + QMutableListIterator it(containments); + + while (it.hasNext()) { + Containment *containment = it.next(); + if (containment == q || + containment->containmentType() == PanelContainment) { + // weed out all containments we don't care about at all + // e.g. Panels and ourself + it.remove(); + continue; + } + + if (q->collidesWithItem(containment)) { + break; + } + } + + if (!it.hasNext()) { + // we made it all the way through the list, we have no + // collisions + return; + } + + // we need to find how many screens are to our top and left + // to calculate the proper offsets for the margins. + int width = 0; + int height = 0; + + QDesktopWidget *desktop = QApplication::desktop(); + int numScreens = desktop->numScreens(); + + for (int i = 0; i < numScreens; ++i) { + QRect otherScreen = desktop->screenGeometry(i); + + if (width < otherScreen.width()) { + width = otherScreen.width(); + } + + if (height < otherScreen.height()) { + height = otherScreen.height(); + } + } + + //this magic number (4) is the number of columns to try before going to the next row + width = (width + INTER_CONTAINMENT_MARGIN) * 4; + height += INTER_CONTAINMENT_MARGIN; + + // a mildly naive "find the first slot" approach + QRectF r = q->boundingRect(); + QPointF topLeft(0, 0); + q->setPos(topLeft); + + positioning = true; + while (true) { + it.toFront(); + + while (it.hasNext()) { + Containment *containment = it.next(); + if (q->collidesWithItem(containment)) { + break; + } + + QPointF pos = containment->pos(); + if (pos.x() <= topLeft.x() && pos.y() <= topLeft.y()) { + // we don't colid with this containment, and it's above + // and to the left of us, so let's not bother checking + // it again if we go through this loop again + it.remove(); + } + } + + if (!it.hasNext()) { + // success! no collisions! + break; + } + + if (topLeft.x() + (r.width() * 2) + INTER_CONTAINMENT_MARGIN > width) { + // we ran out of width room, try another row + topLeft = QPoint(0, topLeft.y() + height); + } else { + topLeft.setX(topLeft.x() + r.width() + INTER_CONTAINMENT_MARGIN); + } + + kDebug() << "trying at" << topLeft; + q->setPos(topLeft); + //kDebug() << collidingItems().count() << collidingItems()[0] << (QGraphicsItem*)this; + } + + positioning = false; +} + +void Containment::Private::positionPanel(bool force) +{ + if (!q->scene()) { + kDebug() << "no scene yet"; + return; + } + + // we position panels in negative coordinates, and stack all horizontal + // and all vertical panels with each other. + + const QPointF p = q->pos(); + + if (!force && + p.y() + q->size().height() < -INTER_CONTAINMENT_MARGIN && + q->scene()->collidingItems(q).isEmpty()) { + // already positioned and not running into any other panels + return; + } + + //TODO: research how non-Horizontal, non-Vertical (e.g. Planar) panels behave here + bool horiz = q->formFactor() == Plasma::Horizontal; + qreal bottom = horiz ? 0 : VERTICAL_STACKING_OFFSET; + qreal lastHeight = 0; + + // this should be ok for small numbers of panels, but we ever end + // up managing hundreds of them, this simplistic alogrithm will + // likely be too slow. + foreach (const Containment* other, q->corona()->containments()) { + if (other == q || + other->containmentType() != PanelContainment || + horiz != (other->formFactor() == Plasma::Horizontal)) { + // only line up with panels of the same orientation + continue; + } + + if (horiz) { + qreal y = other->pos().y(); + if (y < bottom) { + lastHeight = other->size().height(); + bottom = y; + } + } else { + qreal width = other->size().width(); + qreal x = other->pos().x() + width; + if (x > bottom) { + lastHeight = width; + bottom = x + lastHeight; + } + } + } + + kDebug() << "positioning" << (horiz ? "" : "non-") << "horizontal panel; forced?" << force; + // give a space equal to the height again of the last item so there is + // room to grow. + QPointF newPos; + if (horiz) { + bottom -= lastHeight + INTER_CONTAINMENT_MARGIN; + //TODO: fix x position for non-flush-left panels + kDebug() << "moved to" << QPointF(0, bottom - q->size().height()); + newPos = QPointF(0, bottom - q->size().height()); + } else { + bottom += lastHeight + INTER_CONTAINMENT_MARGIN; + //TODO: fix y position for non-flush-top panels + kDebug() << "moved to" << QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); + newPos = QPointF(bottom + q->size().width(), -INTER_CONTAINMENT_MARGIN - q->size().height()); + } + + positioning = true; + if (p != newPos) { + q->setPos(newPos); + emit q->geometryChanged(); + } + positioning = false; +} + + } // Plasma namespace #include "containment.moc" diff --git a/containment.h b/containment.h index 5ebda7971..71435dc19 100644 --- a/containment.h +++ b/containment.h @@ -258,14 +258,6 @@ class PLASMA_EXPORT Containment : public Applet */ bool isToolboxToolEnabled(const QString &toolName) const; - /** - * @internal - * Called when constraints have been updated on this containment to provide - * constraint services common to all containments. Containments should still - * implement their own constraintsUpdated method - */ - void containmentConstraintsUpdated(Plasma::Constraints constraints); - /** * Open the Plasma toolbox */ @@ -394,7 +386,7 @@ class PLASMA_EXPORT Containment : public Applet Q_PRIVATE_SLOT(d, void triggerShowAddWidgets()) Q_PRIVATE_SLOT(d, void handleDisappeared(AppletHandle *handle)) Q_PRIVATE_SLOT(d, void destroyApplet()) - Q_PRIVATE_SLOT(d, void repositionToolbox()) + Q_PRIVATE_SLOT(d, void positionToolBox()) friend class Applet; class Private; diff --git a/containment_p.h b/containment_p.h new file mode 100644 index 000000000..1f239ea10 --- /dev/null +++ b/containment_p.h @@ -0,0 +1,90 @@ +/* + * Copyright 2007 by Aaron Seigo + * Copyright 2008 by Ménard Alexis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef CONTAINMENT_P_H +#define CONTAINMENT_P_H + +static const int INTER_CONTAINMENT_MARGIN = 6; +static const int VERTICAL_STACKING_OFFSET = 10000; + +namespace Plasma +{ + +class Containment; +class Toolbox; + +class Containment::Private +{ +public: + Private(Containment* c) + : q(c), + formFactor(Planar), + location(Floating), + screen(-1), // no screen + toolBox(0), + type(Containment::NoContainmentType), + positioning(false) + { + } + + ~Private() + { + qDeleteAll(applets); + applets.clear(); + } + + Toolbox* createToolBox(); + void positionToolBox(); + void triggerShowAddWidgets(); + + /** + * Called when constraints have been updated on this containment to provide + * constraint services common to all containments. Containments should still + * implement their own constraintsUpdated method + */ + void containmentConstraintsUpdated(Plasma::Constraints constraints); + + bool regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet=0) const; + void positionPanel(bool force = false); + void positionContainment(); + void setLockToolText(); + void handleDisappeared(AppletHandle *handle); + void destroyApplet(); + void appletDestroyed(QObject*); + void appletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim); + + Applet* addApplet(const QString& name, const QVariantList& args = QVariantList(), + const QRectF &geometry = QRectF(-1, -1, -1, -1), uint id = 0, + bool delayedInit = false); + + Containment *q; + FormFactor formFactor; + Location location; + Applet::List applets; + QMap handles; + int screen; + Toolbox *toolBox; + Containment::Type type; + bool positioning; +}; + +} // Plasma namespace + +#endif