diff --git a/plasma/applethandle.cpp b/plasma/applethandle.cpp index bf2410f233..c76a9bc843 100644 --- a/plasma/applethandle.cpp +++ b/plasma/applethandle.cpp @@ -1,632 +1,672 @@ /* * Copyright 2007 by Kevin Ottens * * 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. */ #include "applethandle_p.h" +#include #include #include #include #include #include #include #include #include #include +#include "plasma/view.h" #include "applet.h" #include "containment.h" #include "corona.h" #include "applet.h" #include "theme.h" namespace Plasma { qreal _k_angleForPoints(const QPointF ¢er, const QPointF &pt1, const QPointF &pt2); AppletHandle::AppletHandle(Containment *parent, Applet *applet) : QObject(), QGraphicsItem(parent), m_pressedButton(NoButton), m_containment(parent), m_applet(applet), m_opacity(0.0), m_anim(FadeIn), m_animId(0), m_angle(0.0), m_tempAngle(0.0), m_scaleWidth(1.0), m_scaleHeight(1.0), m_buttonsOnRight(false), m_pendingFade(false) { KColorScheme colors(QPalette::Active, KColorScheme::View, Theme::self()->colors()); m_gradientColor = colors.background(KColorScheme::NormalBackground).color(); QTransform originalMatrix = m_applet->transform(); QRectF rect(m_applet->boundingRect()); QPointF center = rect.center(); originalMatrix.translate(center.x(), center.y()); qreal cosine = originalMatrix.m11(); qreal sine = originalMatrix.m12(); m_angle = _k_angleForPoints(QPointF(0, 0), QPointF(1, 0), QPointF(cosine, sine)); m_applet->resetTransform(); calculateSize(); m_applet->setParentItem(this); rect = QRectF(m_applet->pos(), m_applet->size()); center = rect.center(); QTransform matrix; matrix.translate(center.x(), center.y()); matrix.rotateRadians(m_angle); matrix.translate(-center.x(), -center.y()); setTransform(matrix); m_hoverTimer = new QTimer(this); m_hoverTimer->setSingleShot(true); m_hoverTimer->setInterval(300); connect(m_hoverTimer, SIGNAL(timeout()), this, SLOT(fadeIn())); connect(m_applet, SIGNAL(destroyed(QObject*)), this, SLOT(appletDestroyed())); setAcceptsHoverEvents(true); m_hoverTimer->start(); } AppletHandle::~AppletHandle() { if (m_applet) { m_applet->removeSceneEventFilter(this); QRectF rect = QRectF(m_applet->pos(), m_applet->size()); QPointF center = m_applet->mapFromParent(rect.center()); QPointF newPos = transform().inverted().map(m_applet->pos()); m_applet->setPos(mapToParent(newPos)); QTransform matrix; matrix.translate(center.x(), center.y()); matrix.rotateRadians(m_angle); matrix.translate(-center.x(), -center.y()); m_applet->setTransform(matrix); m_applet->setParentItem(m_containment); m_applet->update(); // re-render the background, now we've transformed the applet } } Applet *AppletHandle::applet() const { return m_applet; } QRectF Plasma::AppletHandle::boundingRect() const { return m_rect; } void AppletHandle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); painter->save(); painter->setOpacity(m_opacity); painter->save(); painter->setOpacity(m_opacity * 0.4); painter->setPen(Qt::NoPen); painter->setRenderHints(QPainter::Antialiasing); QLinearGradient gr(boundingRect().topLeft(), boundingRect().bottomRight()); gr.setColorAt(0, m_gradientColor); gr.setColorAt(0.1, KColorScheme::shade(m_gradientColor, KColorScheme::LightShade)); gr.setColorAt(1, KColorScheme::shade(m_gradientColor, KColorScheme::DarkShade)); painter->setBrush(gr); QPainterPath path = Plasma::roundedRectangle(boundingRect(), 10); if (m_applet) { QPainterPath shape = m_applet->shape(); if (!shape.isEmpty()) { path = path.subtracted(m_applet->mapToParent(m_applet->shape())); } } painter->drawPath(path); painter->restore(); //XXX this code is duplicated in the next function QPointF basePoint = m_rect.topLeft() + QPointF(HANDLE_WIDTH / 2, HANDLE_WIDTH); QPointF step = QPointF(0, ICON_SIZE + ICON_MARGIN); QPointF separator = step + QPointF(0, ICON_MARGIN); if (m_buttonsOnRight) { basePoint += QPointF(m_rect.width() - ICON_SIZE - HANDLE_WIDTH, 0); } //end duplicate code QPointF shiftC; QPointF shiftD; QPointF shiftR; QPointF shiftM; switch(m_pressedButton) { case ConfigureButton: shiftC = QPointF(2, 2); break; case RemoveButton: shiftD = QPointF(2, 2); break; case RotateButton: shiftR = QPointF(2, 2); break; case ResizeButton: shiftM = QPointF(2, 2); break; default: break; } painter->drawPixmap(basePoint + shiftM, KIcon("transform-move").pixmap(ICON_SIZE, ICON_SIZE)); //FIXME no transform-resize icon basePoint += step; painter->drawPixmap(basePoint + shiftR, KIcon("transform-rotate").pixmap(ICON_SIZE, ICON_SIZE)); if (m_applet && m_applet->hasConfigurationInterface()) { basePoint += step; painter->drawPixmap(basePoint + shiftC, KIcon("configure").pixmap(ICON_SIZE, ICON_SIZE)); } basePoint += separator; painter->drawPixmap(basePoint + shiftD, KIcon("edit-delete").pixmap(ICON_SIZE, ICON_SIZE)); painter->restore(); } AppletHandle::ButtonType AppletHandle::mapToButton(const QPointF &point) const { //XXX this code is duplicated in the prev. function QPointF basePoint = m_rect.topLeft() + QPointF(HANDLE_WIDTH / 2, HANDLE_WIDTH); QPointF step = QPointF(0, ICON_SIZE + ICON_MARGIN); QPointF separator = step + QPointF(0, ICON_MARGIN); if (m_buttonsOnRight) { basePoint += QPointF(m_rect.width() - ICON_SIZE - HANDLE_WIDTH, 0); } //end duplicate code QPolygonF activeArea = QPolygonF(QRectF(basePoint, QSizeF(ICON_SIZE, ICON_SIZE))); if (activeArea.containsPoint(point, Qt::OddEvenFill)) { return ResizeButton; } activeArea.translate(step); if (activeArea.containsPoint(point, Qt::OddEvenFill)) { return RotateButton; } if (m_applet && m_applet->hasConfigurationInterface()) { activeArea.translate(step); if (activeArea.containsPoint(point, Qt::OddEvenFill)) { return ConfigureButton; } } activeArea.translate(separator); if (activeArea.containsPoint(point, Qt::OddEvenFill)) { return RemoveButton; } return MoveButton; //return m_applet->mapToParent(m_applet->shape()).contains(point) ? NoButton : MoveButton; } void AppletHandle::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (m_pendingFade) { //m_pendingFade = false; return; } if (event->button() == Qt::LeftButton) { m_pressedButton = mapToButton(event->pos()); //kDebug() << "button pressed:" << m_pressedButton; if (m_pressedButton != NoButton) { // when the mouse goes over a window, the applet is likely to // get a hover out event that we really don't want to respond to // so while we have a button pressed we intercept these events // and handle them ourselves here in AppletHandle m_applet->installSceneEventFilter(this); } event->accept(); update(); return; } QGraphicsItem::mousePressEvent(event); } void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { //kDebug() << "button pressed:" << m_pressedButton << ", fade pending?" << m_pendingFade; if (m_applet) { m_applet->removeSceneEventFilter(this); } if (m_pendingFade) { startFading(FadeOut); m_pendingFade = false; } ButtonType releasedAtButton = mapToButton(event->pos()); if (m_applet && event->button() == Qt::LeftButton) { switch (m_pressedButton) { case ResizeButton: case RotateButton: { if (m_scaleWidth > 0 && m_scaleHeight > 0) { QRectF rect(m_applet->boundingRect()); const qreal newWidth = rect.width() * m_scaleWidth; const qreal newHeight = rect.height() * m_scaleHeight; m_applet->resetTransform(); m_applet->resize(newWidth, newHeight); scale(1.0/m_scaleWidth, 1.0/m_scaleHeight); moveBy((rect.width() - newWidth) / 2, (rect.height() - newHeight) / 2); m_scaleWidth = m_scaleHeight = 0; } QRectF rect = QRectF(m_applet->pos(), m_applet->size()); QPointF center = rect.center(); m_angle += m_tempAngle; m_tempAngle = 0; QTransform matrix; matrix.translate(center.x(), center.y()); matrix.rotateRadians(m_angle); matrix.translate(-center.x(), -center.y()); setTransform(matrix); m_applet->update(); break; } case ConfigureButton: //FIXME: Remove this call once the configuration management change was done if (m_pressedButton == releasedAtButton) { m_containment->emitLaunchActivated(); m_applet->showConfigurationInterface(); } break; case RemoveButton: if (m_pressedButton == releasedAtButton) { forceDisappear(); Phase::self()->animateItem(m_applet, Phase::Disappear); } break; + case MoveButton: { + //find out if we were dropped on a panel or something + QWidget *w = QApplication::topLevelAt(event->screenPos()); + kDebug() << "move to widget" << w; + if (w) { + Plasma::View *v = qobject_cast(w); + if (v) { + Containment *c = v->containment(); + //XXX the dashboard view won't give us a containment. if it did, this could + //break shit. + if (c && c != m_containment) { + //we actually have been dropped on another containment, so move there + //we have a screenpos, we need a scenepos + //FIXME how reliable is this transform? + QPoint pos = v->mapFromGlobal(event->screenPos()); + switchContainment(c, v->mapToScene(pos)); + } + } + } + break; + } default: break; } } m_pressedButton = NoButton; update(); } qreal _k_distanceForPoint(QPointF point) { return std::sqrt(point.x()*point.x()+point.y()*point.y()); } qreal _k_angleForPoints(const QPointF ¢er, const QPointF &pt1, const QPointF &pt2) { QPointF vec1 = pt1 - center; QPointF vec2 = pt2 - center; qreal alpha = std::atan2(vec1.y(), vec1.x()); qreal beta = std::atan2(vec2.y(), vec2.x()); return beta - alpha; } void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { static const qreal snapAngle = M_PI_2 /* $i 3.14159 / 2.0 */; if (!m_applet) { QGraphicsItem::mouseMoveEvent(event); return; } QPointF curPos = transform().map(event->pos()); QPointF lastPos = transform().map(event->lastPos()); QPointF delta = curPos-lastPos; if (m_pressedButton == MoveButton) { setPos(pos()+delta); // test for containment change if (!m_containment->sceneBoundingRect().contains(event->scenePos())) { // see which containment it belongs to Corona * corona = qobject_cast(scene()); if (corona) { QList containments = corona->containments(); for (int i = 0; i < containments.size(); ++i) { if (containments[i]->sceneBoundingRect().contains(event->scenePos())) { // add the applet to the new containment // and take it from the old one - QPointF scenePosition = scenePos(); //kDebug() << "moving to other containment with position" << pos() << event->scenePos(); //kDebug() << "position before reparenting" << pos() << scenePos(); - m_containment = containments[i]; - //m_containment->addChild(m_applet); - //setParentItem(containments[i]); - m_containment->addApplet(m_applet); - setParentItem(m_containment); - m_applet->setParentItem(this); - setPos(m_containment->mapFromScene(scenePosition)); - update(); + switchContainment(containments[i], scenePos()); break; } } } } } else if (m_pressedButton == RotateButton || m_pressedButton == ResizeButton) { if (_k_distanceForPoint(delta) <= 1.0) { return; } QPointF pressPos = mapFromScene(event->buttonDownScenePos(Qt::LeftButton)); QRectF rect = QRectF(m_applet->pos(), m_applet->size()); QPointF center = rect.center(); if (m_pressedButton == RotateButton) { m_tempAngle = _k_angleForPoints(center, pressPos, event->pos()); if (fabs(remainder(m_angle+m_tempAngle, snapAngle)) < 0.15) { m_tempAngle = m_tempAngle - remainder(m_angle+m_tempAngle, snapAngle); } m_scaleWidth = m_scaleHeight = 1.0; } else { qreal w = m_applet->size().width(); qreal h = m_applet->size().height(); QSizeF min = m_applet->minimumSize(); QSizeF max = m_applet->maximumSize(); // If the applet doesn't have a minimum size, calculate based on a // minimum content area size of 16x16 if (min.isEmpty()) { min = m_applet->boundingRect().size() - m_applet->contentRect().size(); min += QSizeF(16, 16); } bool ignoreAspectRatio = m_applet->aspectRatioMode() == Qt::IgnoreAspectRatio; if (QApplication::keyboardModifiers() & Qt::ControlModifier) { ignoreAspectRatio = !ignoreAspectRatio; } if (ignoreAspectRatio) { // free resizing qreal newScaleWidth = 0; qreal newScaleHeight = 0; QPointF startDistance(pressPos - center); QPointF currentDistance(event->pos() - center); newScaleWidth = currentDistance.x() / startDistance.x(); newScaleHeight = currentDistance.y() / startDistance.y(); if (qAbs(w - (newScaleWidth * w)) <= KGlobalSettings::dndEventDelay()) { newScaleWidth = 1.0; } if (qAbs(h - (newScaleHeight * h)) <= KGlobalSettings::dndEventDelay()) { newScaleHeight = 1.0; } if (newScaleHeight * h < min.height()) { m_scaleHeight = min.height() / h; } else if (newScaleHeight * h > max.height()) { m_scaleHeight = max.height() / h; } else { m_scaleHeight = newScaleHeight; } if (newScaleWidth * w < min.width()) { m_scaleWidth = min.width() / w; } else if (newScaleWidth * w > max.width()) { m_scaleWidth = max.width() / w; } else { m_scaleWidth = newScaleWidth; } } else { // maintain aspect ratio qreal newScale = 0; newScale = _k_distanceForPoint(event->pos()-center) / _k_distanceForPoint(pressPos-center); if (qAbs(h - (newScale * h)) <= KGlobalSettings::dndEventDelay()) { newScale = 1.0; } if (newScale * w < min.width() || newScale * h < min.height()) { m_scaleWidth = m_scaleHeight = qMax(min.width() / w, min.height() / h); } else if (newScale * w > max.width() && newScale * h > max.height()) { m_scaleWidth = m_scaleHeight = qMin(max.width() / w, max.height() / h); } else { m_scaleHeight = m_scaleWidth = newScale; } } } QTransform matrix; matrix.translate(center.x(), center.y()); matrix.rotateRadians(m_angle+m_tempAngle); matrix.scale(m_scaleWidth, m_scaleHeight); matrix.translate(-center.x(), -center.y()); setTransform(matrix); } else { QGraphicsItem::mouseMoveEvent(event); } } +//pos relative to scene +void AppletHandle::switchContainment(Containment *containment, const QPointF &pos) +{ + Applet *applet=m_applet; + switch (containment->containmentType()) { + case Containment::PanelContainment: + kDebug() << "panel"; + //we need to fully disassociate the applet and handle, then kill the handle + forceDisappear(); //takes care of event filter and killing handle + m_applet=0; //make sure we don't try to act on the applet again + applet->disconnect(this); //make sure the applet doesn't tell us to do anything + containment->addApplet(applet, containment->mapFromScene(pos)); + break; + default: //FIXME assuming everything else behaves like desktop + kDebug() << "desktop"; + m_containment = containment; + containment->addApplet(m_applet); + setParentItem(containment); + m_applet->setParentItem(this); + setPos(containment->mapFromScene(pos)); + //setPos(pos); + } + update(); +} + QVariant AppletHandle::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == ItemPositionHasChanged && m_applet) { m_applet->constraintsUpdated(Plasma::LocationConstraint); } return QGraphicsItem::itemChange(change, value); } void AppletHandle::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); //kDebug() << "hover enter"; m_hoverTimer->start(); } void AppletHandle::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); //kDebug() << "hover leave" << m_pressedButton; m_hoverTimer->stop(); if (m_pressedButton != NoButton) { m_pendingFade = true; } else { startFading(FadeOut); } } bool AppletHandle::sceneEventFilter(QGraphicsItem *watched, QEvent *event) { if (watched == m_applet && event->type() == QEvent::GraphicsSceneHoverLeave) { hoverLeaveEvent(static_cast(event)); return true; } return false; } void AppletHandle::fadeAnimation(qreal progress) { qreal endOpacity = 1.0; if (m_anim==FadeOut) { endOpacity = 0.0; } m_opacity += (endOpacity-m_opacity)*progress; if (progress>=1.0 && m_anim==FadeOut) { if (m_applet) { m_applet->removeSceneEventFilter(this); } emit disappearDone(this); } update(); } void AppletHandle::fadeIn() { startFading(FadeIn); } void AppletHandle::appletDestroyed() { m_applet = 0; deleteLater(); } void AppletHandle::appletResized() { prepareGeometryChange(); calculateSize(); update(); } void AppletHandle::startFading(FadeType anim) { if (m_animId != 0) { Phase::self()->stopCustomAnimation(m_animId); } qreal time = 250; m_hoverTimer->stop(); if (m_applet) { m_applet->removeSceneEventFilter(this); } if (anim == FadeIn) { time *= 1.0-m_opacity; } else { m_hoverTimer->stop(); time *= m_opacity; } m_anim = anim; m_animId = Phase::self()->customAnimation(40, (int)time, Phase::EaseInOutCurve, this, "fadeAnimation"); } void AppletHandle::forceDisappear() { setAcceptsHoverEvents(false); startFading(FadeOut); } void AppletHandle::calculateSize() { m_rect = m_applet->boundingRect(); m_rect = m_applet->mapToParent(m_rect).boundingRect(); //XXX remember to update this if the number of buttons changes const int requiredHeight = (HANDLE_WIDTH * 2) + m_applet->hasConfigurationInterface() ? ((ICON_SIZE + ICON_MARGIN) * 4) : ((ICON_SIZE + ICON_MARGIN) * 3) + ICON_MARGIN; // that last margin is blank space before the close button if (m_rect.height() < requiredHeight) { float delta = requiredHeight - m_rect.height(); delta = delta/2.0; if (delta > 0.0) { m_rect.adjust(0.0, -delta, 0.0, delta); } } m_rect.adjust(-HANDLE_WIDTH, -HANDLE_WIDTH, HANDLE_WIDTH, HANDLE_WIDTH); if (m_applet->pos().x() <= ((HANDLE_WIDTH * 2) + ICON_SIZE)) { m_rect.adjust(0.0, 0.0, ICON_SIZE, 0.0); m_buttonsOnRight = true; } else { m_rect.adjust(- ICON_SIZE, 0.0, 0.0, 0.0); } } } #include "applethandle_p.moc" diff --git a/plasma/applethandle_p.h b/plasma/applethandle_p.h index 9a80221edf..00aa5d8e2a 100644 --- a/plasma/applethandle_p.h +++ b/plasma/applethandle_p.h @@ -1,97 +1,103 @@ /* * Copyright 2007 by Kevin Ottens * * 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 PLASMA_APPLETHANDLE #define PLASMA_APPLETHANDLE #include #include #include #include "phase.h" #include "svg.h" namespace Plasma { class Applet; class Containment; class AppletHandle : public QObject, public QGraphicsItem { Q_OBJECT public: enum FadeType { FadeIn, FadeOut }; enum ButtonType { NoButton, MoveButton, RotateButton, ConfigureButton, RemoveButton, ResizeButton }; AppletHandle(Containment *parent, Applet *applet); virtual ~AppletHandle(); Applet *applet() const; QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); void startFading(FadeType anim); protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); QVariant itemChange(GraphicsItemChange change, const QVariant &value); bool sceneEventFilter(QGraphicsItem *watched, QEvent *event); Q_SIGNALS: void disappearDone(AppletHandle *self); private Q_SLOTS: void fadeAnimation(qreal progress); void appletDestroyed(); void appletResized(); void fadeIn(); private: static const int HANDLE_WIDTH = 5; static const int ICON_SIZE = 16; static const int ICON_MARGIN = 8; void calculateSize(); ButtonType mapToButton(const QPointF &point) const; void forceDisappear(); + /** + * move our applet to another containment + * @param containment the containment to move to + * @param pos the (scene-relative) position to place it at + */ + void switchContainment(Containment *containment, const QPointF &pos); QRectF m_rect; ButtonType m_pressedButton; Containment *m_containment; Applet *m_applet; qreal m_opacity; FadeType m_anim; Phase::AnimId m_animId; qreal m_angle; qreal m_tempAngle; qreal m_scaleWidth; qreal m_scaleHeight; QColor m_gradientColor; QTimer *m_hoverTimer; bool m_buttonsOnRight; bool m_pendingFade; }; } #endif // multiple inclusion guard diff --git a/plasma/containment.cpp b/plasma/containment.cpp index 64d1397f80..5e5bbbc529 100644 --- a/plasma/containment.cpp +++ b/plasma/containment.cpp @@ -1,878 +1,897 @@ /* * Copyright 2007 by Aaron Seigo * * 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. */ #include "containment.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "applethandle_p.h" #include "corona.h" #include "phase.h" #include "desktoptoolbox_p.h" #include "svg.h" #include "layouts/freelayout.h" #include "layouts/boxlayout.h" namespace Plasma { static const int INTER_CONTAINMENT_MARGIN = 6; class Containment::Private { public: Private(Containment* c) : q(c), formFactor(Planar), location(Floating), screen(-1), toolbox(0), type(Containment::NoContainmentType) { } ~Private() { qDeleteAll(applets); applets.clear(); } DesktopToolbox* createToolbox() { if (!toolbox) { toolbox = new DesktopToolbox(q); toolbox->setPos(q->geometry().width() - toolbox->boundingRect().width(), 0); } return toolbox; } Containment *q; FormFactor formFactor; Location location; Applet::List applets; QMap handles; int screen; DesktopToolbox *toolbox; Containment::Type type; }; Containment::Containment(QGraphicsItem* parent, const QString& serviceId, uint containmentId) : Applet(parent, serviceId, containmentId), d(new Private(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setDrawStandardBackground(false); setContainmentType(CustomContainment); } Containment::Containment(QObject* parent, const QVariantList& args) : Applet(parent, args), d(new Private(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setDrawStandardBackground(false); } Containment::~Containment() { delete d; } void Containment::init() { setCachePaintMode(NoCacheMode); setFlag(QGraphicsItem::ItemIsMovable, false); setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); setAcceptDrops(true); setAcceptsHoverEvents(true); //TODO: would be nice to not do this on init, as it causes Phase to init connect(Phase::self(), SIGNAL(animationComplete(QGraphicsItem*,Plasma::Phase::Animation)), this, SLOT(appletAnimationComplete(QGraphicsItem*,Plasma::Phase::Animation))); if (d->type == NoContainmentType) { setContainmentType(DesktopContainment); } } void Containment::loadConstraints(KConfigGroup* group) { /*kDebug() << "!!!!!!!!!!!!initConstraints" << group->name() << containmentType(); kDebug() << " location:" << group->readEntry("location", (int)d->location); kDebug() << " geom:" << group->readEntry("geometry", geometry()); kDebug() << " formfactor:" << group->readEntry("formfactor", (int)d->formFactor); kDebug() << " screen:" << group->readEntry("screen", d->screen);*/ setGeometry(group->readEntry("geometry", geometry())); setLocation((Plasma::Location)group->readEntry("location", (int)d->location)); setFormFactor((Plasma::FormFactor)group->readEntry("formfactor", (int)d->formFactor)); setScreen(group->readEntry("screen", d->screen)); } void Containment::saveConstraints(KConfigGroup* group) const { // locking is saved in Applet::save group->writeEntry("screen", d->screen); group->writeEntry("formfactor", (int)d->formFactor); group->writeEntry("location", (int)d->location); } void Containment::containmentConstraintsUpdated(Plasma::Constraints constraints) { //kDebug() << "got containmentConstraintsUpdated" << constraints << (QObject*)d->toolbox; if (d->toolbox) { if (constraints & Plasma::ScreenConstraint) { d->toolbox->setPos(geometry().width() - d->toolbox->boundingRect().width(), 0); } if (constraints & Plasma::ImmutableConstraint) { d->toolbox->enableTool("addwidgets", !isImmutable()); } } } Containment::Type Containment::containmentType() const { return d->type; } void Containment::setContainmentType(Containment::Type type) { d->type = type; if (isContainment() && type == DesktopContainment) { if (!d->toolbox) { Plasma::Widget *addWidgetTool = addToolBoxTool("addwidgets", "list-add", i18n("Add Widgets")); connect(addWidgetTool, SIGNAL(clicked()), this, SIGNAL(showAddWidgets())); Plasma::Widget *zoomInTool = addToolBoxTool("zoomIn", "zoom-in", i18n("Zoom In")); connect(zoomInTool, SIGNAL(clicked()), this, SIGNAL(zoomIn())); Plasma::Widget *zoomOutTool = addToolBoxTool("zoomOut", "zoom-out", i18n("Zoom Out")); connect(zoomOutTool, SIGNAL(clicked()), this, SIGNAL(zoomOut())); } } else { delete d->toolbox; d->toolbox = 0; } } Corona* Containment::corona() const { return dynamic_cast(scene()); } void Containment::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { //kDebug() << "let's see if we manage to get a context menu here, huh"; if (!isContainment() || !scene() || !KAuthorized::authorizeKAction("desktop_contextmenu")) { Applet::contextMenuEvent(event); return; } QPointF point = event->scenePos(); QGraphicsItem* item = scene()->itemAt(point); if (item == this) { item = 0; } Applet* applet = 0; while (item) { applet = qgraphicsitem_cast(item); if (applet && !applet->isContainment()) { break; } // applet may have a value due to finding a containment! applet = 0; item = item->parentItem(); } KMenu desktopMenu; //kDebug() << "context menu event " << (QObject*)applet; if (applet) { bool hasEntries = false; QList actions = applet->contextActions(); if (!actions.isEmpty()) { foreach(QAction* action, actions) { desktopMenu.addAction(action); } hasEntries = true; } if (applet->hasConfigurationInterface()) { QAction* configureApplet = new QAction(i18n("%1 Settings", applet->name()), &desktopMenu); configureApplet->setIcon(KIcon("configure")); connect(configureApplet, SIGNAL(triggered(bool)), applet, SLOT(showConfigurationInterface())); desktopMenu.addAction(configureApplet); hasEntries = true; } if (scene() && !static_cast(scene())->isImmutable()) { if (hasEntries) { desktopMenu.addSeparator(); } QAction* closeApplet = new QAction(i18n("Remove this %1", applet->name()), &desktopMenu); QVariant appletV; appletV.setValue((QObject*)applet); closeApplet->setData(appletV); closeApplet->setIcon(KIcon("edit-delete")); connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(destroyApplet())); desktopMenu.addAction(closeApplet); hasEntries = true; } if (!hasEntries) { Applet::contextMenuEvent(event); kDebug() << "no entries"; return; } } else { if (!scene() || (static_cast(scene())->isImmutable() && !KAuthorized::authorizeKAction("unlock_desktop"))) { //kDebug() << "immutability"; Applet::contextMenuEvent(event); return; } QList actions = contextActions(); if (actions.count() < 1) { //kDebug() << "no applet, but no actions"; Applet::contextMenuEvent(event); return; } foreach(QAction* action, actions) { desktopMenu.addAction(action); } } event->accept(); //kDebug() << "executing at" << event->screenPos(); desktopMenu.exec(event->screenPos()); } void Containment::destroyApplet() { QAction *action = qobject_cast(sender()); if (!action) { return; } Applet *applet = qobject_cast(action->data().value()); Phase::self()->animateItem(applet, Phase::Disappear); } void Containment::setFormFactor(FormFactor formFactor) { if (d->formFactor == formFactor && layout()) { return; } //kDebug() << "switching FF to " << formFactor; d->formFactor = formFactor; Layout *lay = layout(); setLayout(0); delete lay; lay = 0; switch (d->formFactor) { case Planar: lay = new FreeLayout(this); break; case Horizontal: lay = new BoxLayout(BoxLayout::LeftToRight, this); lay->setMargin(0); lay->setSpacing(4); break; case Vertical: lay = new BoxLayout(BoxLayout::TopToBottom, this); lay->setMargin(0); lay->setSpacing(4); break; case MediaCenter: //FIXME: need a layout type here! break; default: kDebug() << "This can't be happening! Or... can it? ;)" << d->formFactor; break; } if (lay) { foreach (Applet* applet, d->applets) { lay->addItem(applet); applet->updateConstraints(Plasma::FormFactorConstraint); } } updateConstraints(Plasma::FormFactorConstraint); } FormFactor Containment::formFactor() const { if (isContainment()) { return d->formFactor; } return Applet::formFactor(); } void Containment::setLocation(Location location) { if (d->location == location) { return; } d->location = location; foreach (Applet* applet, d->applets) { applet->updateConstraints(Plasma::LocationConstraint); } setScreen(screen()); updateConstraints(Plasma::LocationConstraint); } Location Containment::location() const { return d->location; } void Containment::clearApplets() { qDeleteAll(d->applets); d->applets.clear(); } Applet* Containment::addApplet(const QString& name, const QVariantList& args, uint id, const QRectF& appletGeometry, bool delayInit) { QGraphicsView *v = view(); if (v) { v->setCursor(Qt::BusyCursor); } Applet* applet = Applet::loadApplet(name, id, args); if (v) { v->unsetCursor(); } if (!applet) { kDebug() << "Applet" << name << "could not be loaded."; applet = new Applet; } - switch (containmentType()) { - case PanelContainment: - { - //panels don't want backgrounds, which is important when setting geometry - applet->setDrawStandardBackground(false); - - // Calculate where the user wants the applet to go before adding it - int index = -1; - QPointF position = appletGeometry.topLeft(); - BoxLayout *l = dynamic_cast(layout()); - if (!delayInit && l && position != QPointF(-1, -1)) { - foreach (Applet *existingApplet, d->applets) { - if (formFactor() == Horizontal) { - qreal middle = (existingApplet->geometry().left() + - existingApplet->geometry().right()) / 2.0; - // Applets are checked in order so there is no need to check - // if the position is equal to or greater than the applet's - // leftmost point. This also allows for dropping in the gap - // between applets. - if (position.x() < middle) { - index = l->indexOf(existingApplet); - break; - } else if (position.x() <= existingApplet->geometry().right()) { - index = l->indexOf(existingApplet) + 1; - break; - } - } else { - qreal middle = (existingApplet->geometry().top() + - existingApplet->geometry().bottom()) / 2.0; - if (position.y() < middle) { - index = l->indexOf(existingApplet); - break; - } else if (position.y() <= existingApplet->geometry().bottom()) { - index = l->indexOf(existingApplet) + 1; - break; - } - } - } - } - - addApplet(applet); - prepareApplet(applet, delayInit); - - // Reposition the applet after adding has been done - if (index != -1) { - l->insertItem(index, l->takeAt(l->indexOf(applet))); - d->applets.removeAll(applet); - d->applets.insert(index, applet); - } - - break; - } - - default: - addApplet(applet); - prepareApplet(applet, delayInit); + addApplet(applet, appletGeometry.topLeft(), delayInit); + if (containmentType() != PanelContainment) { //kDebug() << "adding applet" << applet->name() << "with a default geometry of" << appletGeometry << appletGeometry.isValid(); if (appletGeometry.isValid()) { applet->setGeometry(appletGeometry); } else if (appletGeometry.x() != -1 && appletGeometry.y() != -1) { // yes, this means we can't have items start -1, -1 applet->setGeometry(QRectF(appletGeometry.topLeft(), applet->sizeHint())); } else if (geometry().isValid()) { applet->setGeometry(geometryForApplet(applet)); } } //kDebug() << applet->name() << "sizehint:" << applet->sizeHint() << "geometry:" << applet->geometry(); Corona *c = corona(); if (c) { connect(applet, SIGNAL(configNeedsSaving()), corona(), SLOT(scheduleConfigSync())); } emit appletAdded(applet); 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 +//there. +void Containment::addApplet(Applet *applet, const QPointF &pos, bool dontInit) +{ + if (!applet) { + kDebug() << "adding null applet!?!"; + return; + } + + Containment *currentContainment = applet->containment(); + int index = -1; + + if (containmentType() == PanelContainment) { + //panels don't want backgrounds, which is important when setting geometry + applet->setDrawStandardBackground(false); + + // Calculate where the user wants the applet to go before adding it + //so long as this isn't a new applet with a delayed init + if (! dontInit || (currentContainment && currentContainment != this)) { + index = indexAt(pos); + } + } + + if (currentContainment && currentContainment != this) { + applet->removeSceneEventFilter(currentContainment); + KConfigGroup oldConfig = applet->config(); + applet->resetConfigurationObject(); + currentContainment->d->applets.removeAll(applet); + addChild(applet); + + // now move the old config to the new location + KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); + oldConfig.reparent(&c); + } else { + addChild(applet); + } + + d->applets << applet; + + connect(applet, SIGNAL(destroyed(QObject*)), + this, SLOT(appletDestroyed(QObject*))); + + if (containmentType() == PanelContainment) { + // Reposition the applet after adding has been done + if (index != -1) { + BoxLayout *l = dynamic_cast(layout()); + l->insertItem(index, l->takeAt(l->indexOf(applet))); + d->applets.removeAll(applet); + d->applets.insert(index, applet); + } + } else { + //FIXME if it came from a panel its bg was disabled + //maybe we should expect the applet to handle that on a constraint update? + + //should we really do this? hell, do we have any business playing with the geometry of + //non-panel applets at all? + if (pos != QPointF(-1, -1)) { + applet->setPos(pos); + } + } + prepareApplet(applet, dontInit); //must at least flush constraints +} + +//containment-relative pos... right? +int Containment::indexAt(const QPointF &pos) const +{ + if (pos == QPointF(-1, -1)) { + return -1; + } + BoxLayout *l = dynamic_cast(layout()); + if (l) { + foreach (Applet *existingApplet, d->applets) { + if (formFactor() == Horizontal) { + qreal middle = (existingApplet->geometry().left() + + existingApplet->geometry().right()) / 2.0; + // Applets are checked in order so there is no need to check + // if the position is equal to or greater than the applet's + // leftmost point. This also allows for dropping in the gap + // between applets. + if (pos.x() < middle) { + return l->indexOf(existingApplet); + } else if (pos.x() <= existingApplet->geometry().right()) { + return l->indexOf(existingApplet) + 1; + } + } else { + qreal middle = (existingApplet->geometry().top() + + existingApplet->geometry().bottom()) / 2.0; + if (pos.y() < middle) { + return l->indexOf(existingApplet); + } else if (pos.y() <= existingApplet->geometry().bottom()) { + return l->indexOf(existingApplet) + 1; + } + } + } + } + return -1; +} + void Containment::prepareApplet(Applet *applet, bool delayInit) { if (delayInit) { if (containmentType() == DesktopContainment) { applet->installSceneEventFilter(this); } } else { applet->init(); Phase::self()->animateItem(applet, Phase::Appear); } applet->updateConstraints(Plasma::AllConstraints); if (!delayInit) { applet->flushUpdatedConstraints(); } } QRectF Containment::geometryForApplet(Applet *applet) const { // The value part of these maps isn't used. Only sorted keys are needed. QMap xPositions; QMap yPositions; // Add the top-left corner offset by the applet's border QPointF offset = applet->boundingRect().topLeft(); xPositions[-offset.x()] = true; yPositions[-offset.y()] = true; QRectF placement(QPointF(0, 0), applet->sizeHint()); foreach (Applet *existingApplet, d->applets) { QPointF bottomRight = existingApplet->geometry().bottomRight(); if (bottomRight.x() + placement.width() < geometry().width()) { xPositions[bottomRight.x() + 1] = true; } if (bottomRight.y() + placement.height() < geometry().height()) { yPositions[bottomRight.y() + 1] = true; } } // Try to fit it in an empty space foreach (qreal x, xPositions.keys()) { foreach (qreal y, yPositions.keys()) { placement.moveTo(x, y); if (regionIsEmpty(placement, applet)) { return placement; } } } // Otherwise place it in the centre of the screen placement.moveLeft(geometry().width() / 2 - placement.width() / 2); placement.moveTop(geometry().height() / 2 - placement.height() / 2); return placement; } bool Containment::regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet) const { foreach (Applet *applet, d->applets) { if (applet != ignoredApplet && applet->geometry().intersects(region)) { return false; } } return true; } -void Containment::addApplet(Applet *applet) -{ - Containment *currentContainment = applet->containment(); - if (currentContainment && currentContainment != this) { - applet->removeSceneEventFilter(currentContainment); - KConfigGroup oldConfig = applet->config(); - applet->resetConfigurationObject(); - currentContainment->d->applets.removeAll(applet); - addChild(applet); - - // now move the old config to the new location - KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); - oldConfig.reparent(&c); - } else { - addChild(applet); - } - - d->applets << applet; - - if (currentContainment) { - applet->installSceneEventFilter(this); - } - - connect(applet, SIGNAL(destroyed(QObject*)), - this, SLOT(appletDestroyed(QObject*))); -} - void Containment::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); d->applets.removeAll(applet); emit appletRemoved(applet); } void Containment::appletAnimationComplete(QGraphicsItem *item, Plasma::Phase::Animation anim) { if (anim == Phase::Disappear) { QGraphicsItem *parent = item->parentItem(); while (parent) { if (parent == this) { Applet *applet = qgraphicsitem_cast(item); if (applet) { applet->destroy(); } break; } parent = parent->parentItem(); } } else if (anim == Phase::Appear) { if (containmentType() == DesktopContainment && item->parentItem() == this && qgraphicsitem_cast(item)) { item->installSceneEventFilter(this); } } } Applet::List Containment::applets() const { return d->applets; } void Containment::setScreen(int screen) { // screen of -1 means no associated screen. // sanity check to make sure someone else doesn't have this screen already! if (screen > -1 && containmentType() == DesktopContainment && corona()) { Containment* currently = corona()->containmentForScreen(screen); if (currently && currently != this) { //kDebug() << "currently is on screen" << currently->screen() << "and is" << currently->name() << (QObject*)currently << (QObject*)this; currently->setScreen(-1); } } //kDebug() << "setting screen to" << screen << "and we are a" << containmentType(); QDesktopWidget *desktop = QApplication::desktop(); int numScreens = desktop->numScreens(); if (screen < -1) { screen = -1; } //kDebug() << "setting screen to " << screen << "and type is" << containmentType(); if (screen < numScreens && screen > -1) { QRect r = desktop->screenGeometry(screen); if (containmentType() == DesktopContainment) { // we need to find how many screens are to our top and left // to calculate the proper offsets for the margins. int x = r.x(); int y = r.y(); int screensLeft = 0; int screensAbove = 0; for (int i = 0; i < numScreens; ++i) { QRect otherScreen = desktop->screenGeometry(screen); if (x > otherScreen.x()) { ++screensLeft; } if (y > otherScreen.y()) { ++screensAbove; } } r.moveLeft(r.x() + INTER_CONTAINMENT_MARGIN * screensLeft); r.moveTop(r.y() + INTER_CONTAINMENT_MARGIN * screensAbove); // FIXME: positioning at this x,y will break if we switch between containments for a // given screen! we should change the pos() on new containment setup. setGeometry(r); //kDebug() << "setting geometry to" << desktop->screenGeometry(screen) << r << geometry(); } else if (containmentType() == PanelContainment) { QRect r = desktop->screenGeometry(screen); //kDebug() << "we are a panel on" << r << ", let's move ourselves to a negative coordinate system" << -(r.y() * 2) - r.height() - INTER_CONTAINMENT_MARGIN; // panels are moved into negative coords; we double the x() so that each screen get's // it's own area for panels int vertOffset = (r.y() * 2) + r.height() + INTER_CONTAINMENT_MARGIN; translate(0, -vertOffset); } } d->screen = screen; updateConstraints(Plasma::ScreenConstraint); } int Containment::screen() const { return d->screen; } KPluginInfo::List Containment::knownContainments(const QString &category, const QString &parentApp) { QString constraint; if (parentApp.isEmpty()) { constraint.append("not exist [X-KDE-ParentApp]"); } else { constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); } if (!category.isEmpty()) { if (!constraint.isEmpty()) { constraint.append(" and "); } constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'"); if (category == "Miscellaneous") { constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')"); } } KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); //kDebug() << "constraint was" << constraint << "which got us" << offers.count() << "matches"; return KPluginInfo::fromServices(offers); } KPluginInfo::List Containment::knownContainmentsForMimetype(const QString &mimetype) { QString constraint = QString("'%1' in MimeTypes").arg(mimetype); //kDebug() << "knownContainmentsForMimetype with" << mimetype << constraint; KService::List offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); return KPluginInfo::fromServices(offers); } void Containment::dropEvent(QGraphicsSceneDragDropEvent *event) { //kDebug() << "drop event:" << event->mimeData()->text(); QString mimetype(static_cast(scene())->appletMimeType()); if (event->mimeData()->hasFormat(mimetype) && scene()) { QString plasmoidName; plasmoidName = event->mimeData()->data(mimetype); QRectF geom(mapFromScene(event->scenePos()), QSize(0, 0)); addApplet(plasmoidName, QVariantList(), 0, geom); event->acceptProposedAction(); } else if (KUrl::List::canDecode(event->mimeData())) { KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); foreach (const KUrl& url, urls) { KMimeType::Ptr mime = KMimeType::findByUrl(url); QString mimeName = mime->name(); QRectF geom(event->scenePos(), QSize(0, 0)); QVariantList args; args << url.url(); // kDebug() << mimeName; KPluginInfo::List appletList = Applet::knownAppletsForMimetype(mimeName); if (appletList.isEmpty()) { // no special applet associated with this mimetype, let's addApplet("icon", args, 0, geom); } else { //TODO: should we show a dialog here to choose which plasmoid load if //appletList.count() > 0? addApplet(appletList.first().pluginName(), args, 0, geom); } } event->acceptProposedAction(); } } void Containment::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { //FIXME Qt4.4 check to see if this is still necessary to avoid unecessary repaints // check with QT_FLUSH_PAINT=1 and mouse through applets that accept hover, // applets that don't and system windows if (event->spontaneous()) { Applet::hoverEnterEvent(event); } Q_UNUSED(event) } void Containment::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { //FIXME Qt4.4 check to see if this is still necessary to avoid unecessary repaints // check with QT_FLUSH_PAINT=1 and mouse through applets that accept hover, // applets that don't and system windows // Applet::hoverLeaveEvent(event); Q_UNUSED(event) } bool Containment::sceneEventFilter(QGraphicsItem *watched, QEvent *event) { Applet *applet = qgraphicsitem_cast(watched); // Otherwise we're watching something we shouldn't be... //kDebug() << "got sceneEvent"; Q_ASSERT(applet!=0); if (!d->applets.contains(applet)) { return false; } switch (event->type()) { case QEvent::GraphicsSceneHoverEnter: //kDebug() << "got hoverenterEvent" << isImmutable() << " " << applet->isImmutable(); if (!isImmutable() && !applet->isImmutable()) { if (d->handles.contains(applet)) { d->handles[applet]->startFading(AppletHandle::FadeIn); } else { //kDebug() << "generated applet handle"; //TODO: there should be a small delay on showing these. they pop up too quickly/easily // right now AppletHandle *handle = new AppletHandle(this, applet); d->handles[applet] = handle; connect(handle, SIGNAL(disappearDone(AppletHandle*)), this, SLOT(handleDisappeared(AppletHandle*))); connect(applet, SIGNAL(geometryChanged()), handle, SLOT(appletResized())); } } break; case QEvent::GraphicsSceneHoverLeave: //kDebug() << "got hoverLeaveEvent"; if (d->handles.contains(applet)) { QGraphicsSceneHoverEvent *he = static_cast(event); if (!d->handles[applet]->boundingRect().contains(d->handles[applet]->mapFromScene(he->scenePos()))) { d->handles[applet]->startFading(AppletHandle::FadeOut); } } default: break; } return false; } void Containment::handleDisappeared(AppletHandle *handle) { d->handles.remove(handle->applet()); handle->deleteLater(); } void Containment::emitLaunchActivated() { kDebug(); emit launchActivated(); } Plasma::Widget * Containment::addToolBoxTool(const QString& toolName, const QString& iconName, const QString& iconText) { Plasma::Icon *tool = new Plasma::Icon(this); tool->setDrawBackground(true); tool->setIcon(KIcon(iconName)); tool->setText(iconText); tool->setOrientation(Qt::Horizontal); QSizeF iconSize = tool->sizeFromIconSize(22); tool->setMinimumSize(iconSize); tool->setMaximumSize(iconSize); tool->resize(tool->sizeHint()); d->createToolbox()->addTool(tool, toolName); return tool; } void Containment::enableToolBoxTool(const QString &toolname, bool enable) { d->createToolbox()->enableTool(toolname, enable); } bool Containment::isToolboxToolEnabled(const QString &toolname) const { return d->createToolbox()->isToolEnabled(toolname); } void Containment::showToolbox() { d->createToolbox()->showToolbox(); } void Containment::hideToolbox() { d->createToolbox()->hideToolbox(); } } // Plasma namespace #include "containment.moc" diff --git a/plasma/containment.h b/plasma/containment.h index 911887fc5e..bdd76445c0 100644 --- a/plasma/containment.h +++ b/plasma/containment.h @@ -1,352 +1,360 @@ /* * Copyright 2007 by Aaron Seigo * * 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 PLASMA_CONTAINMENT_H #define PLASMA_CONTAINMENT_H #include #include #include #include #include #include #include #include "widgets/icon.h" namespace Plasma { class AppletHandle; class DataEngine; class Package; class Corona; /** * @short The base class for plugins that provide backgrounds and applet grouping containers * * Containment objects provide the means to group applets into functional sets. * They also provide the following: * * creation of focussing event * - drawing of the background image (which can be interactive) * - form factors (e.g. panel, desktop, full screen, etc) * - applet layout management * * Since containment is actually just a Plasma::Applet, all the techniques used * for writing the visual presentation of Applets is applicable to Containtments. * Containments are differentiated from Applets by being marked with the ServiceType * of Plasma/Containment. Plugins registered with both the Applet and the Containment * ServiceTypes can be loaded for us in either situation. * * See techbase.kde.org for a tutorial on writing Containments using this class. */ class PLASMA_EXPORT Containment : public Applet { Q_OBJECT public: typedef QList List; typedef QHash Dict; enum Type { NoContainmentType = -1 /**< @internal */, DesktopContainment = 0 /**< A desktop containment */, PanelContainment /**< A desktop panel */, CustomContainment /**< A containment that is neither a desktop nor a panel, but something application specific */ }; /** * @arg parent the QGraphicsItem this applet is parented to * @arg serviceId the name of the .desktop file containing the * information about the widget * @arg appletId a unique id used to differentiate between multiple * instances of the same Applet type */ explicit Containment(QGraphicsItem* parent = 0, const QString& serviceId = QString(), uint containmentId = 0); /** * This constructor is to be used with the plugin loading systems * found in KPluginInfo and KService. The argument list is expected * to have two elements: the KService service ID for the desktop entry * and an applet ID which must be a base 10 number. * * @arg parent a QObject parent; you probably want to pass in 0 * @arg args a list of strings containing two entries: the service id * and the applet id */ Containment(QObject* parent, const QVariantList& args); ~Containment(); /** * Reimplemented from Applet */ void init(); /** * Returns the type of containment */ Type containmentType() const; /** * Sets the type of this containment. */ void setContainmentType(Containment::Type type); /** * Returns the current form factor the applets in this Containment * are being displayed in. * * @see Plasma::FormFactor */ FormFactor formFactor() const; /** * Returns the location of this Containment * * @see Plasma::Location */ Location location() const; /** * Returns a list of all known containments. * * @param category Only applets matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only applets not specifically * registered to an application. * @return list of applets **/ static KPluginInfo::List knownContainments(const QString &category = QString(), const QString &parentApp = QString()); /** * Returns a list of all known applets associated with a certain mimetype * * @return list of applets **/ static KPluginInfo::List knownContainmentsForMimetype(const QString &mimetype); /** * Adds an applet to this Containment * * @param name the plugin name for the applet, as given by * KPluginInfo::pluginName() * @param args argument list to pass to the plasmoid * @param id to assign to this applet, or 0 to auto-assign it a new id * @param geometry where to place the applet, or to auto-place it if an invalid * is provided * @param delayedInit if true, init() will not be called on the applet * * @return a pointer to the applet on success, or 0 on failure */ Applet* addApplet(const QString& name, const QVariantList& args = QVariantList(), uint id = 0, const QRectF &geometry = QRectF(-1, -1, -1, -1), bool delayedInit = false); /** * @return the applets currently in this Containment */ Applet::List applets() const; /** * Removes all applets from this Containment */ void clearApplets(); /** - * add existing applet to this containment + * add existing applet to this containment at pos + * @param pos the containment-relative position + * @param dontInit if true, init() will not be called on the applet */ - void addApplet(Applet * applet); + void addApplet(Applet *applet, const QPointF &pos = QPointF(-1, -1), bool dontInit = true); + + /** + * @return the index to insert an applet at if you want it near the point pos. + * @param pos the containment-relative position + */ + virtual int indexAt(const QPointF &pos) const; /** * Sets the physical screen this Containment is associated with. * * @param screen the screen number this containment is the desktop for, or -1 * if it is not serving as the desktop for any screen */ void setScreen(int screen); /** * @return the screen number this containment is serving as the desktop for * or -1 if none */ int screen() const; /** * @internal */ void saveConstraints(KConfigGroup* group) const; /** * @internal */ void loadConstraints(KConfigGroup* group); /** * Emits the launchActivated() signal */ void emitLaunchActivated(); /** * Constructs a toolbox item and adds it to the toolbox. The toolbox takes over ownership of the item. Returns the constructed tool. * * @arg name of the tool * @arg name of the icon * @arg text to be displayed on the icon * * @return the constructed tool */ Plasma::Widget * addToolBoxTool(const QString &toolName = QString(), const QString &iconName = QString(), const QString &iconText = QString()); /** * Enables or disables a toolbox tool by name * * @arg name the name of the tool * @arg enable true to enable, false to disable */ void enableToolBoxTool(const QString &toolname, bool enable); /** * Returns whether or not a given toolbox tool is enabled */ 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 */ void showToolbox(); /** * Close the Plasma toolbox */ void hideToolbox(); Q_SIGNALS: /** * This signal is emitted when a new applet is created by the containment */ void appletAdded(Plasma::Applet* applet); /** * This signal is emitted when an applet is destroyed */ void appletRemoved(Plasma::Applet* applet); /** * This signal indicates that an application launch, window * creation or window focus event was triggered. This is used, for instance, * to ensure that the Dashboard view in Plasma hides when such an event is * triggered by an item it is displaying. */ void launchActivated(); /** * Emitted when the containment requests zooming out one step. * Usually only used for desktop containments. */ void zoomIn(); /** * Emitted when the containment requests zooming out one step. * Usually only used for desktop containments. */ void zoomOut(); /** * Emitted when the containment requests an add widgets dialog is shown. * Usually only used for desktop containments. */ void showAddWidgets(); public Q_SLOTS: /** * Informs the Corona as to what position it is in. This is informational * only, as the Corona doesn't change it's actual location. This is, * however, passed on to Applets that may be managed by this Corona. * * @param location the new location of this Corona */ void setLocation(Plasma::Location location); /** * Sets the form factor for this Corona. This may cause changes in both * the arrangement of Applets as well as the display choices of individual * Applets. */ void setFormFactor(Plasma::FormFactor formFactor); /** * Returns the Corona (if any) that this Containment is hosted by */ Corona* corona() const; protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent * event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); bool sceneEventFilter(QGraphicsItem *watched, QEvent *event); protected Q_SLOTS: /** * @internal */ void appletDestroyed(QObject*); void appletAnimationComplete(QGraphicsItem *item, Plasma::Phase::Animation anim); void dropEvent(QGraphicsSceneDragDropEvent* event); private Q_SLOTS: void handleDisappeared(AppletHandle *handle); void destroyApplet(); private: QRectF geometryForApplet(Applet *applet) const; bool regionIsEmpty(const QRectF ®ion, Applet *ignoredApplet=0) const; void prepareApplet(Applet *applet, bool delayInit); Q_DISABLE_COPY(Containment) class Private; Private* const d; }; } // Plasma namespace #endif // multiple inclusion guard