2008-09-07 23:33:02 +02:00
|
|
|
/*
|
2008-09-08 19:32:25 +02:00
|
|
|
* Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
|
2008-09-07 23:33:02 +02:00
|
|
|
*
|
2008-09-08 19:32:25 +02:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2008-09-07 23:33:02 +02:00
|
|
|
*
|
2008-09-08 19:32:25 +02:00
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
2008-09-07 23:33:02 +02:00
|
|
|
*
|
2008-09-08 19:32:25 +02:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301 USA
|
2008-09-07 23:33:02 +02:00
|
|
|
*/
|
2008-08-02 13:48:56 +02:00
|
|
|
|
|
|
|
#include "extender.h"
|
|
|
|
|
|
|
|
#include <QAction>
|
2008-08-02 19:46:32 +02:00
|
|
|
#include <QGraphicsGridLayout>
|
|
|
|
#include <QGraphicsLinearLayout>
|
|
|
|
|
|
|
|
#include "applet.h"
|
|
|
|
#include "containment.h"
|
|
|
|
#include "corona.h"
|
|
|
|
#include "extenderitem.h"
|
|
|
|
#include "widgets/label.h"
|
2008-08-02 13:48:56 +02:00
|
|
|
|
2008-08-02 19:46:32 +02:00
|
|
|
#include "private/applet_p.h"
|
|
|
|
#include "private/extender_p.h"
|
2008-08-02 13:48:56 +02:00
|
|
|
|
|
|
|
namespace Plasma
|
|
|
|
{
|
|
|
|
|
|
|
|
Extender::Extender(Applet *applet)
|
|
|
|
: QGraphicsWidget(applet),
|
|
|
|
d(new ExtenderPrivate(applet, this))
|
|
|
|
{
|
|
|
|
applet->d->extender = this;
|
2008-08-23 19:47:50 +02:00
|
|
|
d->layout = new QGraphicsLinearLayout(this);
|
2008-08-02 13:48:56 +02:00
|
|
|
d->layout->setOrientation(Qt::Vertical);
|
|
|
|
setLayout(d->layout);
|
|
|
|
|
|
|
|
d->emptyExtenderLabel = new Label(this);
|
|
|
|
d->emptyExtenderLabel->setText(d->emptyExtenderMessage);
|
2008-08-29 21:00:10 +02:00
|
|
|
d->emptyExtenderLabel->setMinimumSize(QSizeF(150, 64));
|
|
|
|
d->emptyExtenderLabel->setPreferredSize(QSizeF(200, 64));
|
|
|
|
d->emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
2008-08-02 13:48:56 +02:00
|
|
|
d->layout->addItem(d->emptyExtenderLabel);
|
|
|
|
|
2008-08-29 21:09:27 +02:00
|
|
|
d->adjustSizeHints();
|
|
|
|
|
2008-08-02 13:48:56 +02:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-05 19:28:58 +02:00
|
|
|
QVariant Extender::itemChange(GraphicsItemChange change, const QVariant &value)
|
|
|
|
{
|
|
|
|
if (change == QGraphicsItem::ItemPositionHasChanged) {
|
|
|
|
emit geometryChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
return QGraphicsWidget::itemChange(change, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
|
|
|
|
{
|
|
|
|
QGraphicsWidget::resizeEvent(event);
|
|
|
|
emit geometryChanged();
|
|
|
|
}
|
|
|
|
|
2008-08-02 13:48:56 +02:00
|
|
|
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);
|
2008-08-28 16:32:48 +02:00
|
|
|
d->emptyExtenderLabel->hide();
|
2008-08-02 13:48:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
d->adjustSizeHints();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Extender::itemRemovedEvent(ExtenderItem *item)
|
|
|
|
{
|
|
|
|
kDebug() << "Removing item from layout.";
|
|
|
|
|
|
|
|
d->layout->removeItem(item);
|
|
|
|
|
|
|
|
//add the empty extender message if needed.
|
2008-08-28 16:32:48 +02:00
|
|
|
if (!attachedItems().count()) {
|
|
|
|
d->emptyExtenderLabel->show();
|
2008-09-05 15:28:03 +02:00
|
|
|
d->emptyExtenderLabel->setMinimumSize(item->size());
|
2008-08-28 16:32:48 +02:00
|
|
|
//just in case:
|
|
|
|
d->layout->removeItem(d->emptyExtenderLabel);
|
2008-08-02 13:48:56 +02:00
|
|
|
d->layout->addItem(d->emptyExtenderLabel);
|
|
|
|
}
|
|
|
|
|
|
|
|
d->adjustSizeHints();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Extender::itemHoverEnterEvent(ExtenderItem *item)
|
|
|
|
{
|
2008-08-28 16:32:48 +02:00
|
|
|
itemHoverMoveEvent(item, QPointF(0, 0));
|
2008-08-02 13:48:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2008-09-05 15:28:03 +02:00
|
|
|
widget->setPreferredSize(QSizeF(item->size().width(), item->size().height()));
|
2008-08-02 13:48:56 +02:00
|
|
|
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);
|
2008-08-28 16:32:48 +02:00
|
|
|
d->emptyExtenderLabel->hide();
|
2008-08-02 13:48:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2008-08-28 16:32:48 +02:00
|
|
|
if (!attachedItems().count()) {
|
|
|
|
d->emptyExtenderLabel->show();
|
2008-09-05 15:28:03 +02:00
|
|
|
d->emptyExtenderLabel->setMinimumSize(item->size());
|
2008-08-28 16:32:48 +02:00
|
|
|
d->layout->removeItem(d->emptyExtenderLabel);
|
2008-08-02 13:48:56 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // Plasma namespace
|
|
|
|
|
|
|
|
#include "extender.moc"
|