plasma-framework/extender.cpp
Rob Scheepmaker 3bfb1bf662 Fixed a type, making destroy() function again. Move the destruction of temporary extender
items to applet, since in extender's dtor, applet's config isn't accessible anymore. Only 
fire itemDetached when an item is really moved, not only when being dragged away.


svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=841509
2008-08-03 12:16:41 +00:00

370 lines
11 KiB
C++

/***************************************************************************
* Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, 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 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 "extender.h"
#include <QAction>
#include <QGraphicsGridLayout>
#include <QGraphicsLinearLayout>
#include "applet.h"
#include "containment.h"
#include "corona.h"
#include "extenderitem.h"
#include "widgets/label.h"
#include "private/applet_p.h"
#include "private/extender_p.h"
namespace Plasma
{
Extender::Extender(Applet *applet)
: QGraphicsWidget(applet),
d(new ExtenderPrivate(applet, this))
{
applet->d->extender = this;
d->layout = new QGraphicsLinearLayout();
d->layout->setOrientation(Qt::Vertical);
setLayout(d->layout);
d->emptyExtenderLabel = new Label(this);
d->emptyExtenderLabel->setText(d->emptyExtenderMessage);
d->emptyExtenderLabel->setMinimumSize(QSizeF(150, 24));
d->emptyExtenderLabel->setPreferredSize(QSizeF(200, 48));
d->emptyExtenderLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
d->layout->addItem(d->emptyExtenderLabel);
d->loadExtenderItems();
}
Extender::~Extender()
{
delete d;
}
void Extender::setEmptyExtenderMessage(const QString &message)
{
d->emptyExtenderMessage = message;
if (d->emptyExtenderLabel) {
d->emptyExtenderLabel->setText(message);
}
}
QString Extender::emptyExtenderMessage() const
{
return d->emptyExtenderMessage;
}
QList<ExtenderItem*> Extender::items() const
{
QList<ExtenderItem*> result;
//iterate through all extenders we can find and check each extenders source applet.
foreach (Containment *c, d->applet->containment()->corona()->containments()) {
foreach (Applet *applet, c->applets()) {
if (applet->extender()) {
foreach (ExtenderItem *item, applet->extender()->attachedItems()) {
if (item->sourceAppletId() == d->applet->id()) {
result.append(item);
}
}
}
}
}
return result;
}
QList<ExtenderItem*> Extender::attachedItems() const
{
return d->attachedExtenderItems;
}
QList<ExtenderItem*> Extender::detachedItems() const
{
QList<ExtenderItem*> result;
foreach (ExtenderItem *item, items()) {
if (item->isDetached()) {
result.append(item);
}
}
return result;
}
ExtenderItem *Extender::item(const QString &name) const
{
foreach (ExtenderItem *item, items()) {
if (item->config().readEntry("extenderItemName", "") == name) {
return item;
}
}
return 0;
}
void Extender::saveState()
{
kDebug() << "saving state";
foreach (ExtenderItem *item, attachedItems()) {
item->config().writeEntry("extenderItemPosition", item->geometry().y());
}
}
void Extender::itemAddedEvent(ExtenderItem *item, const QPointF &pos)
{
kDebug() << "Adding item to layout.";
//this is a sane size policy imo.
item->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
if (pos == QPointF(-1, -1)) {
d->layout->addItem(item);
} else {
d->layout->insertItem(d->insertIndexFromPos(pos), item);
}
//remove the empty extender message if needed.
if (d->emptyExtenderLabel) {
d->layout->removeItem(d->emptyExtenderLabel);
delete d->emptyExtenderLabel;
d->emptyExtenderLabel = 0;
}
d->adjustSizeHints();
}
void Extender::itemRemovedEvent(ExtenderItem *item)
{
kDebug() << "Removing item from layout.";
d->layout->removeItem(item);
//add the empty extender message if needed.
if (!attachedItems().count() && !d->emptyExtenderLabel) {
d->emptyExtenderLabel = new Label(this);
d->emptyExtenderLabel->setText(d->emptyExtenderMessage);
d->layout->addItem(d->emptyExtenderLabel);
}
d->adjustSizeHints();
}
void Extender::itemHoverEnterEvent(ExtenderItem *item)
{
Q_UNUSED(item);
}
void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
{
int insertIndex = d->insertIndexFromPos(pos);
if (insertIndex == d->currentSpacerIndex) {
//relayouting is resource intensive, so don't do that when not necesarry
return;
}
//Make sure we remove any spacer that might allready be in the layout.
itemHoverLeaveEvent(item);
d->currentSpacerIndex = insertIndex;
//Create a widget that functions as spacer, and add that to the layout.
QGraphicsWidget *widget = new QGraphicsWidget(this);
widget->setPreferredSize(QSizeF(150, item->size().height()));
widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
d->spacerWidget = widget;
d->layout->insertItem(insertIndex, widget);
//Make sure we remove any 'no detachables' label that might be there, and update the layout.
//XXX: duplicated from itemAttachedEvent.
if (d->emptyExtenderLabel) {
d->layout->removeItem(d->emptyExtenderLabel);
delete d->emptyExtenderLabel;
d->emptyExtenderLabel = 0;
}
d->adjustSizeHints();
}
void Extender::itemHoverLeaveEvent(ExtenderItem *item)
{
Q_UNUSED(item);
if (d->spacerWidget) {
//Remove any trace of the spacer widget.
d->layout->removeItem(d->spacerWidget);
delete d->spacerWidget;
d->spacerWidget = 0;
d->currentSpacerIndex = -1;
//Make sure we add a 'no detachables' label when the layout is empty.
if (!attachedItems().count() && !d->emptyExtenderLabel) {
d->emptyExtenderLabel = new Label(this);
d->emptyExtenderLabel->setText(d->emptyExtenderMessage);
d->layout->addItem(d->emptyExtenderLabel);
}
d->adjustSizeHints();
}
}
ExtenderPrivate::ExtenderPrivate(Applet *applet, Extender *extender) :
q(extender),
applet(applet),
spacerWidget(0),
emptyExtenderMessage(i18n("no items")),
emptyExtenderLabel(0),
popup(false)
{
}
ExtenderPrivate::~ExtenderPrivate()
{
}
void ExtenderPrivate::addExtenderItem(ExtenderItem *item, const QPointF &pos)
{
attachedExtenderItems.append(item);
item->action("returntosource")->setVisible(item->isDetached());
q->itemAddedEvent(item, pos);
q->itemHoverLeaveEvent(item);
emit q->itemAttached(item);
}
void ExtenderPrivate::removeExtenderItem(ExtenderItem *item)
{
attachedExtenderItems.removeOne(item);
q->itemRemovedEvent(item);
}
void ExtenderPrivate::adjustSizeHints()
{
if (!q->layout()) {
return;
}
q->layout()->updateGeometry();
q->setMinimumSize(q->layout()->minimumSize());
q->setPreferredSize(q->layout()->preferredSize());
q->setMaximumSize(q->layout()->maximumSize());
q->updateGeometry();
emit q->geometryChanged();
}
int ExtenderPrivate::insertIndexFromPos(const QPointF &pos) const
{
int insertIndex = -1;
//XXX: duplicated from panel
if (pos != QPointF(-1, -1)) {
for (int i = 0; i < layout->count(); ++i) {
QRectF siblingGeometry = layout->itemAt(i)->geometry();
qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
if (pos.y() < middle) {
insertIndex = i;
break;
} else if (pos.y() <= siblingGeometry.bottom()) {
insertIndex = i + 1;
break;
}
}
}
return insertIndex;
}
void ExtenderPrivate::loadExtenderItems()
{
KConfigGroup cg = applet->config("ExtenderItems");
//first create a list of extenderItems, and then sort them on their position, so the items get
//recreated in the correct order.
//TODO: this restoring of the correct order should now be done in itemAddedEvent instead of
//here, to allow easy subclassing of Extender.
QList<QPair<int, QString> > groupList;
foreach (const QString &extenderItemId, cg.groupList()) {
KConfigGroup dg = cg.group(extenderItemId);
groupList.append(qMakePair(dg.readEntry("extenderItemPosition", 0), extenderItemId));
}
qSort(groupList);
//iterate over the extender items
for (int i = 0; i < groupList.count(); i++) {
QPair<int, QString> pair = groupList[i];
KConfigGroup dg = cg.group(pair.second);
//load the relevant settings.
QString extenderItemId = dg.name();
QString extenderItemName = dg.readEntry("extenderItemName", "");
QString appletName = dg.readEntry("sourceAppletPluginName", "");
uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
bool temporarySourceApplet = false;
//find the source applet.
Corona *corona = applet->containment()->corona();
Applet *sourceApplet = 0;
foreach (Containment *containment, corona->containments()) {
foreach (Applet *applet, containment->applets()) {
if (applet->id() == sourceAppletId) {
sourceApplet = applet;
}
}
}
//There is no source applet. We just instantiate one just for the sake of creating
//detachables.
if (!sourceApplet) {
kDebug() << "creating a temporary applet as factory";
sourceApplet = Applet::load(appletName);
temporarySourceApplet = true;
//TODO: maybe add an option to applet to indicate that it shouldn't be deleted after
//having used it as factory.
}
if (!sourceApplet) {
kDebug() << "sourceApplet is null? appletName = " << appletName;
kDebug() << " extenderItemId = " << extenderItemId;
} else {
ExtenderItem *item = new ExtenderItem(q, extenderItemId.toInt());
sourceApplet->initExtenderItem(item);
if (temporarySourceApplet) {
delete sourceApplet;
}
}
}
adjustSizeHints();
}
QGraphicsGridLayout *ExtenderPrivate::s_popupLayout = 0;
} // Plasma namespace
#include "extender.moc"