Extenders enter trunk! The implementation still has some rough edges and fixmes, but the api

is in quite nice shape and basides a couple of bugs, it basically works. So what are you 
still doing reading this? Port your favourite applet to extenders today! ;)


svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=840978
This commit is contained in:
Rob Scheepmaker 2008-08-02 11:48:56 +00:00
parent db8bb180e8
commit a6293d363f
15 changed files with 1794 additions and 5 deletions

View File

@ -63,6 +63,8 @@ set(plasma_LIB_SRCS
view.cpp
widgets/checkbox.cpp
widgets/combobox.cpp
widgets/extender.cpp
widgets/extenderitem.cpp
widgets/flash.cpp
widgets/groupbox.cpp
widgets/icon.cpp
@ -163,6 +165,8 @@ install(FILES
install(FILES
widgets/checkbox.h
widgets/combobox.h
widgets/extender.h
widgets/extenderitem.h
widgets/flash.h
widgets/groupbox.h
widgets/icon.h
@ -203,6 +207,8 @@ includes/DataEngineManager
includes/DataEngineScript
includes/Delegate
includes/Dialog
includes/Extender
includes/ExtenderItem
includes/Flash
includes/GroupBox
includes/Icon

View File

@ -72,6 +72,8 @@
#include "view.h"
#include "widgets/label.h"
#include "widgets/pushbutton.h"
#include "widgets/extender.h"
#include "widgets/extenderitem.h"
#include "tooltipmanager.h"
#include "private/containment_p.h"
@ -177,6 +179,11 @@ void Applet::save(KConfigGroup &g) const
}
KConfigGroup appletConfigGroup(&group, "Configuration");
if (extender()) {
extender()->saveState();
}
//FIXME: we need a global save state too
saveState(appletConfigGroup);
@ -186,6 +193,7 @@ void Applet::save(KConfigGroup &g) const
}
}
void Applet::restore(KConfigGroup &group)
{
QList<qreal> m = group.readEntry("transform", QList<qreal>());
@ -459,6 +467,17 @@ void Applet::constraintsEvent(Plasma::Constraints constraints)
}
}
void Applet::initExtenderItem(ExtenderItem *item)
{
Q_UNUSED(item)
item->destroy();
}
Extender *Applet::extender() const
{
return d->extender;
}
QString Applet::name() const
{
if (!d->appletDescription.isValid()) {
@ -1460,6 +1479,7 @@ bool Applet::isContainment() const
AppletPrivate::AppletPrivate(KService::Ptr service, int uniqueID, Applet *applet)
: appletId(uniqueID),
q(applet),
extender(0),
backgroundHints(Applet::StandardBackground),
appletDescription(service),
package(0),

View File

@ -48,6 +48,8 @@ class Containment;
class DataEngine;
class Package;
class AppletPrivate;
class ExtenderItem;
class Extender;
/**
* @short The base Applet class
@ -511,7 +513,7 @@ class PLASMA_EXPORT Applet : public QGraphicsWidget
* can coordinate themselves with these changes if they desire.
*/
void geometryChanged();
/**
* Emitted by Applet subclasses when they change a sizeHint and wants to announce the change
*/
@ -583,6 +585,27 @@ class PLASMA_EXPORT Applet : public QGraphicsWidget
**/
virtual void init();
/**
* Get's called when and extender item has to be initialized after a plasma restart. If you
* create ExtenderItems in your applet, you should implement this function to again create
* the widget that should be shown in this extender item. This function might look something
* like this:
*
* @code
* SuperCoolWidget *widget = new SuperCoolWidget();
* dataEngine("engine")->connectSource(item->config("dataSourceName"), widget);
* item->setWidget(widget);
* @endcode
*
* You can also add one or more custom qactions to this extender item in this function.
*/
virtual void initExtenderItem(ExtenderItem *item);
/**
* @return the extender this applet has.
*/
Extender *extender() const;
protected:
/**
* This constructor is to be used with the plugin loading systems
@ -675,6 +698,7 @@ class PLASMA_EXPORT Applet : public QGraphicsWidget
*/
virtual void constraintsEvent(Plasma::Constraints constraints);
/**
* Register the widgets that manage mouse clicks but you still want
* to be able to drag the applet around when holding the mouse pointer
@ -771,6 +795,10 @@ class PLASMA_EXPORT Applet : public QGraphicsWidget
friend class AppletScript;
friend class AppletHandle;
friend class AppletPrivate;
//FIXME: this shouldn't be necesarry.
friend class Extender;
friend class ExtenderItem;
};
} // Plasma namespace

View File

@ -23,6 +23,7 @@
#include <QApplication>
#include <QGraphicsSceneDragDropEvent>
#include <QGraphicsGridLayout>
#include <QMimeData>
#include <QPainter>
#include <QTimer>
@ -50,7 +51,8 @@ public:
: q(corona),
immutability(Mutable),
mimetype("text/x-plasmoidservicename"),
config(0)
config(0),
offscreenLayout(0)
{
if (KGlobal::hasMainComponent()) {
configName = KGlobal::mainComponent().componentName() + "-appletsrc";
@ -171,6 +173,7 @@ public:
KSharedConfigPtr config;
QTimer configSyncTimer;
QList<Containment*> containments;
QGraphicsGridLayout *offscreenLayout;
};
Corona::Corona(QObject *parent)
@ -338,6 +341,43 @@ Containment* Corona::addContainmentDelayed(const QString& name, const QVariantLi
return d->addContainment(name, args, 0, true);
}
void Corona::addOffscreenWidget(QGraphicsWidget *widget)
{
kDebug() << "adding offscreen widget.";
widget->setParentItem(0);
if (!d->offscreenLayout) {
kDebug() << "adding offscreen widget.";
QGraphicsWidget *offscreenWidget = new QGraphicsWidget(0);
addItem(offscreenWidget);
d->offscreenLayout = new QGraphicsGridLayout(offscreenWidget);
//FIXME: do this a nice way.
offscreenWidget->setPos(-10000, -10000);
offscreenWidget->setLayout(d->offscreenLayout);
}
d->offscreenLayout->addItem(widget, d->offscreenLayout->rowCount() + 1,
d->offscreenLayout->columnCount() + 1);
d->offscreenLayout->invalidate();
kDebug() << "current scenerect = " << widget->sceneBoundingRect();
}
void Corona::removeOffscreenWidget(QGraphicsWidget *widget)
{
if (!d->offscreenLayout) {
return;
}
for (int i = 0; i < d->offscreenLayout->count(); i++) {
QGraphicsWidget *foundWidget =
dynamic_cast<QGraphicsWidget*>(d->offscreenLayout->itemAt(i));
if (foundWidget == widget) {
d->offscreenLayout->removeAt(i);
}
}
}
void Corona::loadDefaultLayout()
{
}

View File

@ -27,6 +27,8 @@
#include <plasma/plasma.h>
#include <plasma/plasma_export.h>
class QGraphicsGridLayout;
namespace Plasma
{
@ -93,6 +95,20 @@ public:
*/
Containment* containmentForScreen(int screen) const;
/**
* Adds a widget in the topleft quadrant in the scene. Widgets in the topleft quadrant are
* normally never shown unless you specifically aim a view at it, which makes it ideal for
* toplevel views etc.
* @param widget the widget to add.
*/
void addOffscreenWidget(QGraphicsWidget *widget);
/**
* Removes a widget from the topleft quadrant in the scene.
* @param widget the widget to remove.
*/
void removeOffscreenWidget(QGraphicsWidget *widget);
public Q_SLOTS:
/**
* Initializes the layout from a config file. This will first clear any existing

2
includes/Extender Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/widgets/extender.h"

2
includes/ExtenderItem Normal file
View File

@ -0,0 +1,2 @@
#include "../../plasma/widgets/extenderitem.h"

View File

@ -75,6 +75,7 @@ public:
// number of members at this point.
uint appletId;
Applet *q;
Extender *extender;
Applet::BackgroundHints backgroundHints;
KPluginInfo appletDescription;
Package* package;

377
widgets/extender.cpp Normal file
View File

@ -0,0 +1,377 @@
/***************************************************************************
* 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_p.h"
#include "extender.h"
#include <QGraphicsLinearLayout>
#include <QGraphicsGridLayout>
#include <QAction>
#include "plasma/applet.h"
#include "plasma/applet_p.h"
#include "plasma/containment.h"
#include "plasma/corona.h"
#include "plasma/widgets/extenderitem.h"
#include "plasma/widgets/label.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()
{
//FIXME: this used to work, not anymore... fix.
foreach (ExtenderItem *item, attachedItems()) {
if (!item->isDetached() && item->autoExpireDelay()) {
//destroy temporary extender items, so their configuration won't linger after a plasma
//restart.
item->destroy();
}
}
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);
emit q->itemDetached(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"

177
widgets/extender.h Normal file
View File

@ -0,0 +1,177 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef EXTENDER_H
#define EXTENDER_H
#include <QGraphicsWidget>
#include "plasma/plasma_export.h"
namespace Plasma
{
class ExtenderPrivate;
class ExtenderItem;
class Applet;
/**
* An extender is a widget where items can be added to. These so called ExtenderItems can be
* detached by the user an dropped in other extenders or somewhere else, in which case a new
* extenderapplet is created and the item is added there.
* This widget allows using ExtenderItems in you applet. Extender takes care of the presentation
* of a collection of extenderitems, and keeps track of extenderItems that originated in it.
* This default extender displays extender items in a vertical layout, and shows spacers in this
* layout when hovering with an extender item over it, which is probably good for most cases.
* If you wish to have a different presentation of extender items, you can choose to subclass
* this class. In this case you'll need to reimplement the extenderItem* events and optionally
* the saveState function.
*/
class PLASMA_EXPORT Extender : public QGraphicsWidget
{
Q_OBJECT
Q_PROPERTY(QString emptyExtenderMessage READ emptyExtenderMessage
WRITE setEmptyExtenderMessage)
public:
/**
* Creates an extender. Note that extender expects applet to have a config(), and needs a
* scene because of that. So you should only instantiate an extender in init() or later, not
* in an applet's constructor.
* @param applet The applet this extender is part of. Null is not allowed here.
*/
explicit Extender(Applet *applet);
~Extender();
/**
* @param emptyExtenderMessage The text to be shown whenever the applet's extender is empty.
* Defaults to i18n'ed "no items".
*/
void setEmptyExtenderMessage(const QString &message);
/**
* @return The text to be shown whenever the applet's layout is empty.
*/
QString emptyExtenderMessage() const;
/**
* @returns a list of all extender items (attached AND detached) where the source applet is
* this applet.
*/
QList<ExtenderItem*> items() const;
/**
* @returns a list of all attached extender items.
*/
QList<ExtenderItem*> attachedItems() const;
/**
* @returns a list of all detached extender items.
*/
QList<ExtenderItem*> detachedItems() const;
/**
* This function can be used for easily determining if a certain item is allready displayed
* in a extender item somewhere, so your applet doesn't duplicate this item. Say the applet
* displays 'jobs', from an engine which add's a source for every job. In sourceAdded you
* could do something like:
* if (!extenderItem(source)) {
* //add an extender item monitoring this source.
* }
*/
ExtenderItem *item(const QString &name) const;
protected:
/**
* Get's called after an item has been added to this extender. The bookkeeping has allready
* been done when this function get's called. The only thing left to do is put it somewhere
* appropriate. The default implementation adds the extenderItem to the appropriate place in
* a QGraphicsLinearLayout.
* @param item The item that has just been added.
* @param pos The location the item has been dropped in local coordinates.
*/
virtual void itemAddedEvent(ExtenderItem *item, const QPointF &pos);
/**
* Get's called after an item has been removed from this extender. All bookkeeping has
* allready been done when this function get's called.
* @param item The item that has just been removed.
*/
virtual void itemRemovedEvent(ExtenderItem *item);
/**
* Get's called when an ExtenderItem that get's dragged enters this extender. Default
* implementation does nothing.
*/
virtual void itemHoverEnterEvent(ExtenderItem *item);
/**
* Gets called when an ExtenderItem is hovering over this extender. Implement this function
* to give some visual feedback about what will happen when the mouse button is released at
* that position. The default implementation shows a spacer at the appropriate location in
* the layout.
* @param item The item that's hovering over this extender. Most usefull for obtaining the
* size of the spacer.
* @param pos The location the item is hovering.
*/
virtual void itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos);
/**
* Get's called when an ExtenderItem that was previously hovering over this extender moves
* away from this extender. The default implementation removes any spacer from the layout.
*/
virtual void itemHoverLeaveEvent(ExtenderItem *item);
/**
* This function get's called for every extender when plasma exits. Implement this function
* to store the current state of this extender (position in a layout for example), so this
* can be restored when applet starts again. The default implementation stores the y
* coordinate of every extender item in the config field extenderItemPos.
*/
virtual void saveState();
Q_SIGNALS:
/**
* Fires when an extender item is added to this extender.
*/
void itemAttached(Plasma::ExtenderItem *);
/**
* Fires when an extender item is removed from this extender.
*/
void itemDetached(Plasma::ExtenderItem *);
/**
* Fires when an extender's preferred size changes.
*/
void geometryChanged();
private:
ExtenderPrivate* const d;
friend class ExtenderPrivate;
friend class ExtenderItem;
friend class ExtenderItemPrivate;
//applet should be able to call saveState();
friend class Applet;
};
} // Plasma namespace
#endif //EXTENDER_H

73
widgets/extender_p.h Normal file
View File

@ -0,0 +1,73 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef LIBS_PLASMA_EXTENDER_P_H
#define LIBS_PLASMA_EXTENDER_P_H
#include <QString>
#include <QList>
#include <QPointF>
class QGraphicsGridLayout;
class QGraphicsLinearLayout;
class QGraphicsWidget;
namespace Plasma
{
class Applet;
class Extender;
class ExtenderItem;
class Label;
class ExtenderPrivate
{
public:
ExtenderPrivate(Applet *applet, Extender *q);
~ExtenderPrivate();
void addExtenderItem(ExtenderItem *item, const QPointF &pos = QPointF(-1, -1));
void removeExtenderItem(ExtenderItem *item);
void adjustSizeHints();
int insertIndexFromPos(const QPointF &pos) const;
void loadExtenderItems();
Extender *q;
Applet *applet;
QGraphicsLinearLayout *layout;
int currentSpacerIndex;
QGraphicsWidget *spacerWidget;
QString emptyExtenderMessage;
Label *emptyExtenderLabel;
uint sourceAppletId;
QList<ExtenderItem*> attachedExtenderItems;
bool popup;
static QGraphicsGridLayout *s_popupLayout;
};
} // namespace Plasma
#endif // LIBS_PLASMA_EXTENDER_P_H

827
widgets/extenderitem.cpp Normal file
View File

@ -0,0 +1,827 @@
/***************************************************************************
* 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 "extenderitem.h"
#include <QtGui/QPainter>
#include <QtGui/QGraphicsSceneResizeEvent>
#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QGraphicsLinearLayout>
#include <QApplication>
#include <QAction>
#include <QTimer>
#include <KDE/KDebug>
#include <KDE/KIcon>
#include <KDE/KWindowSystem>
#include "plasma/corona.h"
#include "plasma/containment.h"
#include "plasma/theme.h"
#include "plasma/view.h"
#include "plasma/applet.h"
#include "plasma/applet_p.h"
#include "plasma/panelsvg.h"
#include "plasma/widgets/extender.h"
#include "plasma/widgets/extender_p.h"
namespace Plasma
{
class ExtenderItemPrivate
{
public:
ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
: q(extenderItem),
widget(0),
toplevel(0),
previousTargetExtender(0),
extender(hostExtender),
title(QString()),
sourceAppletId(hostExtender->d->applet->id()),
mousePressed(false),
expirationTimer(0)
{
}
~ExtenderItemPrivate()
{
delete toplevel;
delete appletBackground;
delete dragger;
}
/**
* @return a Rect containing the area of the detachable where the draghandle can be drawn.
*/
QRectF dragHandleRect()
{
qreal left, top, right, bottom;
dragger->getMargins(left, top, right, bottom);
QRectF rect(0, 0, q->size().width(), top + bottom +
dragger->elementSize("hint-preferred-icon-size").height());
return rect;
}
QRectF titleRect()
{
qreal left, top, right, bottom;
dragger->getMargins(left, top, right, bottom);
return dragHandleRect().adjusted(left + collapseIcon->size().width(), top,
-toolbox->size().width(), -bottom);
}
//XXX kinda duplicated from applethandle.
//returns true if the applet overlaps with a different view then the current one.
bool leaveCurrentView(const QRect &rect)
{
foreach (QWidget *widget, QApplication::topLevelWidgets()) {
if (widget->geometry().intersects(rect)) {
//is this widget a plasma view, a different view then our current one,
//AND not a dashboardview?
Plasma::View *v = qobject_cast<View*>(widget);
Plasma::View *currentV = 0;
if (hostApplet()) {
currentV = qobject_cast<View*>(hostApplet()->containment()->view());
}
if (v && v != currentV
&& v->containment() != hostApplet()->containment()) {
return true;
}
}
}
return false;
}
QRect screenRect() {
return hostApplet()->containment()->view()
->mapFromScene(q->sceneBoundingRect()).boundingRect();
}
void toggleCollapse() {
q->setCollapsed(!q->isCollapsed());
}
void updateToolBox() {
if (toolbox && dragger && toolboxLayout) {
kDebug() << "updating toolbox";
//clean the layout.
uint iconHeight = dragger->elementSize("hint-preferred-icon-size").height();
while (toolboxLayout->count()) {
QGraphicsLayoutItem *icon = toolboxLayout->itemAt(0);
QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(icon);
widget->deleteLater();
toolboxLayout->removeAt(0);
}
//add the actions that are actually set to visible.
foreach (QAction *action, actions) {
if (action->isVisible()) {
Icon *icon = new Icon(q);
icon->setAction(action);
QSizeF iconSize = icon->sizeFromIconSize(iconHeight);
icon->setMinimumSize(iconSize);
icon->setMaximumSize(iconSize);
toolboxLayout->addItem(icon);
}
}
toolboxLayout->updateGeometry();
qreal left, top, right, bottom;
dragger->getMargins(left, top, right, bottom);
//position the toolbox correctly.
QSizeF minimum = toolboxLayout->minimumSize();
toolbox->setPos(q->size().width() - minimum.width(),
((dragger->size().height() + top + bottom)/2) -
(minimum.height()/2));
}
}
//TODO: something like this as static function in corona might be a good idea.
QPointF scenePosFromScreenPos(const QPoint &pos) const
{
//get the stacking order of the toplevel windows and remove the toplevel view that's
//only here while dragging, since we're not interested in finding that.
QList<WId> order = KWindowSystem::stackingOrder();
if (toplevel) {
order.removeOne(toplevel->winId());
}
QGraphicsView *found = 0;
foreach (QWidget *w, QApplication::topLevelWidgets()) {
QGraphicsView *v = qobject_cast<QGraphicsView *>(w);
if (v && v->isVisible() && v->geometry().contains(pos)) {
if (found) {
if (order.indexOf(found->winId()) < order.indexOf(v->winId())) {
found = v;
}
} else {
found = v;
}
}
}
Q_ASSERT(found);
return found->mapToScene(found->mapFromGlobal(pos));
}
Applet *hostApplet() const
{
if (extender) {
return extender->d->applet;
} else {
return 0;
}
}
ExtenderItem *q;
QGraphicsWidget *widget;
QGraphicsWidget *toolbox;
QGraphicsLinearLayout *toolboxLayout;
QGraphicsView *toplevel;
Extender *previousTargetExtender;
Extender *extender;
Applet *sourceApplet;
KConfigGroup config;
PanelSvg *dragger;
PanelSvg *appletBackground;
Icon *collapseIcon;
QAction *returnAction;
QMap<QString, QAction*> actions;
QString title;
QString name;
uint sourceAppletId;
uint extenderItemId;
QPointF deltaScene;
QPoint mousePos;
bool mousePressed;
bool mouseOver;
QTimer *expirationTimer;
static uint s_maxExtenderItemId;
};
uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
ExtenderItem::ExtenderItem(Extender *hostExtender, uint extenderItemId)
: QGraphicsWidget(hostExtender),
d(new ExtenderItemPrivate(this, hostExtender))
{
Q_ASSERT(hostExtender);
//set the extenderId
if (extenderItemId) {
d->extenderItemId = extenderItemId;
ExtenderItemPrivate::s_maxExtenderItemId =
qMax(ExtenderItemPrivate::s_maxExtenderItemId, extenderItemId);
} else {
d->extenderItemId = ++ExtenderItemPrivate::s_maxExtenderItemId;
}
d->sourceApplet = hostExtender->d->applet;
//create items's configgroup
if (hostExtender->d->applet) {
KConfigGroup cg = hostExtender->d->applet->config("ExtenderItems");
KConfigGroup dg = KConfigGroup(&cg, QString::number(d->extenderItemId));
if (!dg.readEntry("sourceAppletId", 0)) {
//The item is new
dg.writeEntry("sourceAppletPluginName", hostExtender->d->applet->pluginName());
dg.writeEntry("sourceAppletId", hostExtender->d->applet->id());
d->sourceAppletId = hostExtender->d->applet->id();
d->sourceApplet = hostExtender->d->applet;
} else {
//The item allready exists.
d->name = dg.readEntry("extenderItemName", "");
d->sourceAppletId = dg.readEntry("sourceAppletId", 0);
//Set the sourceapplet.
Corona *corona = hostExtender->d->applet->containment()->corona();
foreach (Containment *containment, corona->containments()) {
foreach (Applet *applet, containment->applets()) {
if (applet->id() == d->sourceAppletId &&
applet->pluginName() == dg.readEntry("sourceAppletPluginName", "")) {
d->sourceApplet = applet;
}
}
}
}
}
//create the dragger and standard applet background.
d->dragger = new PanelSvg();
d->dragger->setImagePath("widgets/dragger");
d->appletBackground = new PanelSvg();
d->appletBackground->setImagePath("widgets/background");
d->appletBackground->setEnabledBorders(0);
//create the toolbox.
d->toolbox = new QGraphicsWidget(this);
d->toolboxLayout = new QGraphicsLinearLayout();
d->toolbox->setLayout(d->toolboxLayout);
//allow the theme to set the size of the icon.
//TODO: discuss with others to determine details of the theming implementation. I don't really
//like this approach, but it works...
QSizeF iconSize = d->dragger->elementSize("hint-preferred-icon-size");
qreal left, top, right, bottom;
d->dragger->getMargins(left, top, right, bottom);
//create the collapse/applet icon.
d->collapseIcon = new Icon(KIcon(hostExtender->d->applet->icon()), "", this);
d->collapseIcon->resize(d->collapseIcon->sizeFromIconSize(iconSize.height()));
d->collapseIcon->setPos(left, (d->dragger->size().height() + top + bottom)/2 -
d->collapseIcon->size().height()/2);
connect(d->collapseIcon, SIGNAL(clicked()), this, SLOT(toggleCollapse()));
//Add the return to source action.
d->returnAction = new QAction(this);
d->returnAction->setIcon(KIcon("returntosource"));
d->returnAction->setEnabled(true);
d->returnAction->setVisible(true);
connect(d->returnAction, SIGNAL(triggered()), this, SLOT(moveBackToSource()));
addAction("returntosource", d->returnAction);
//set the extender we want to move to.
setExtender(hostExtender);
setCollapsed(false); //sets the size hints.
setAcceptHoverEvents(true);
d->updateToolBox();
updateGeometry();
}
ExtenderItem::~ExtenderItem()
{
delete d;
}
KConfigGroup ExtenderItem::config() const
{
KConfigGroup cg = extender()->d->applet->config("ExtenderItems");
return KConfigGroup(&cg, QString::number(d->extenderItemId));
}
void ExtenderItem::setTitle(const QString &title)
{
d->title = title;
update();
}
QString ExtenderItem::title() const
{
return d->title;
}
void ExtenderItem::setName(const QString &name)
{
d->name = name;
config().writeEntry("extenderItemName", name);
}
QString ExtenderItem::name() const
{
return d->name;
}
void ExtenderItem::setWidget(QGraphicsWidget *widget)
{
widget->setParentItem(this);
widget->setPos(QPointF(0, d->dragHandleRect().height()));
d->widget = widget;
setCollapsed(isCollapsed()); //updates the size hints.
}
QGraphicsWidget *ExtenderItem::widget() const
{
return d->widget;
}
void ExtenderItem::setIcon(const QIcon &icon)
{
d->collapseIcon->setIcon(icon);
}
void ExtenderItem::setIcon(const QString &icon)
{
d->collapseIcon->setIcon(icon);
}
QIcon ExtenderItem::icon() const
{
return d->collapseIcon->icon();
}
void ExtenderItem::setExtender(Extender *extender, const QPointF &pos)
{
kDebug() << "setExtender";
//We are switching extender...
//first remove this item from the old extender.
if (d->extender) {
d->extender->d->removeExtenderItem(this);
}
//move the configuration.
if (d->hostApplet() && (extender != d->extender)) {
kDebug() << "moving configuration";
KConfigGroup c = extender->d->applet->config("ExtenderItems");
kDebug() << "hostAppletId is " << d->hostApplet()->id();
kDebug() << "config name is " << config().name();
config().reparent(&c);
}
d->extender = extender;
//change parent.
setParentItem(extender);
extender->d->addExtenderItem(this, pos);
//cancel the timer.
if (d->expirationTimer && isDetached()) {
d->expirationTimer->stop();
delete d->expirationTimer;
d->expirationTimer = 0;
}
}
Extender *ExtenderItem::extender() const
{
return d->extender;
}
bool ExtenderItem::isCollapsed() const
{
if (!d->widget) {
return true;
} else {
return !d->widget->isVisible();
}
}
void ExtenderItem::setAutoExpireDelay(uint time)
{
if (!isDetached()) {
d->expirationTimer = new QTimer(this);
d->expirationTimer->setSingleShot(true);
d->expirationTimer->setInterval(time);
connect(d->expirationTimer, SIGNAL(timeout()), this, SLOT(destroy()));
d->expirationTimer->start();
}
}
bool ExtenderItem::autoExpireDelay() const
{
if (d->expirationTimer) {
return d->expirationTimer->interval();
} else {
return 0;
}
}
bool ExtenderItem::isDetached() const
{
if (d->hostApplet()) {
return (sourceAppletId() != d->hostApplet()->id());
} else {
return false;
}
}
void ExtenderItem::addAction(const QString &name, QAction *action)
{
Q_ASSERT(action);
d->actions[name] = action;
connect(action, SIGNAL(changed()), this, SLOT(updateToolBox()));
d->updateToolBox();
}
QAction *ExtenderItem::action(const QString &name) const
{
if (d->actions.contains(name)) {
return d->actions[name];
} else {
return 0;
}
}
uint ExtenderItem::sourceAppletId() const
{
return d->sourceAppletId;
}
void ExtenderItem::destroy()
{
kDebug() << "deleting config group.";
d->hostApplet()->config("ExtenderItem").deleteGroup(QString::number(d->extenderItemId));
if (d->extender) {
d->extender->d->removeExtenderItem(this);
}
deleteLater();
}
void ExtenderItem::setCollapsed(bool collapsed)
{
if (!d->widget) {
setPreferredSize(QSizeF(200, d->dragHandleRect().height()));
setMinimumSize(QSizeF(0, d->dragHandleRect().height()));
//FIXME: wasn't there some sort of QWIDGETMAXSIZE thingy?
setMaximumSize(QSizeF(1000, d->dragHandleRect().height()));
updateGeometry();
return;
}
qreal left, top, right, bottom;
d->dragger->getMargins(left, top, right, bottom);
d->widget->setVisible(!collapsed);
if (collapsed) {
setPreferredSize(QSizeF(d->widget->preferredWidth(), d->dragHandleRect().height()));
setMinimumSize(QSizeF(d->widget->minimumWidth(), d->dragHandleRect().height()));
setMaximumSize(QSizeF(d->widget->maximumWidth(), d->dragHandleRect().height()));
//FIXME: why don't tooltips work?
//d->collapseIcon->setToolTip(i18n("Expand this widget"));
} else {
setPreferredSize(QSizeF(d->widget->preferredWidth(),
d->widget->preferredHeight() + d->dragHandleRect().height()));
setMinimumSize( QSizeF(d->widget->minimumWidth(),
d->widget->minimumHeight() + d->dragHandleRect().height()));
setMaximumSize( QSizeF(d->widget->maximumWidth(),
d->widget->maximumHeight() + d->dragHandleRect().height()));
//d->collapseIcon->setToolTip(i18n("Collapse this widget"));
}
if (d->extender) {
d->extender->d->adjustSizeHints();
}
}
void ExtenderItem::moveBackToSource()
{
if (!d->sourceApplet) {
return;
}
setExtender(d->sourceApplet->d->extender);
}
void ExtenderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setRenderHint(QPainter::TextAntialiasing, true);
painter->setRenderHint(QPainter::Antialiasing, true);
if (d->mousePressed) {
//only paint the standard applet background when dragging the thing around.
d->appletBackground->paintPanel(painter); //, QRectF(QPointF(0,0), size()));
}
d->dragger->paintPanel(painter); //, d->dragHandleRect());
//draw the title.
Plasma::Theme *theme = Plasma::Theme::defaultTheme();
QFont font = theme->font(Plasma::Theme::DefaultFont);
font.setPointSize(font.pointSize() - 2);
//XXX: duplicated from windowtaskitem.
//TODO: hmm, create something generic for this... there's probably more stuff that wants to have
//this functionality
QRectF rect = QRectF(d->titleRect().width() - 30, 0, 30, d->titleRect().height());
QPixmap pixmap(d->titleRect().size().toSize());
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
p.setPen(theme->color(Plasma::Theme::TextColor));
p.setFont(font);
p.drawText(QRectF(QPointF(0, 0), d->titleRect().size()),
Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignLeft,
d->title);
// Create the alpha gradient for the fade out effect
QLinearGradient alphaGradient(0, 0, 1, 0);
alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
//TODO: correct handling of right to left text.
alphaGradient.setColorAt(0, QColor(0, 0, 0, 255));
alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.fillRect(rect, alphaGradient);
p.end();
painter->drawPixmap(d->titleRect().topLeft(), pixmap);
}
void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
{
qreal left, top, right, bottom;
d->dragger->getMargins(left, top, right, bottom);
//resize the dragger
QSizeF newDraggerSize = event->newSize();
newDraggerSize.setHeight(d->dragger->elementSize("hint-preferred-icon-size").height() + top + bottom);
d->dragger->resizePanel(newDraggerSize);
//resize the applet background
d->appletBackground->resizePanel(event->newSize());
//resize the widget
if (d->widget) {
QSizeF newWidgetSize = event->newSize();
newWidgetSize.setHeight(newWidgetSize.height() - d->dragger->size().height() - top - bottom);
d->widget->resize(newWidgetSize);
}
d->updateToolBox();
}
void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (!(d->dragHandleRect().contains(event->pos()))) {
return;
}
d->mousePressed = true;
d->deltaScene = pos();
Applet *parentApplet = d->hostApplet();
QGraphicsView *v = parentApplet->view();
QPoint localpos = v->mapFromScene(scenePos());
d->mousePos = event->screenPos() - v->mapToGlobal(localpos);
QPointF mousePos = v->mapToScene(v->mapFromGlobal(event->screenPos()));
parentApplet->raise();
setZValue(parentApplet->zValue());
d->extender->d->removeExtenderItem(this);
if (d->extender) {
d->extender->itemHoverEnterEvent(this);
d->extender->itemHoverMoveEvent(this, d->extender->mapFromScene(mousePos));
}
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
}
void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
if (!d->mousePressed) {
return;
}
//keep track of the movement in scene coordinates. we use this to set the position of the
//applet when remaining in the current view.
d->deltaScene += event->scenePos() - event->lastScenePos();
//set a rect in screencoordinates so we can check when we need to move to a toplevel
//view.
QRect screenRect = QRect();
screenRect.setTopLeft(event->screenPos() - d->mousePos);
screenRect.setSize(d->screenRect().size());
if (d->leaveCurrentView(screenRect)) {
//we're moving the applet to a toplevel view, so place it somewhere out of sight
//first: in the topleft quadrant.
if (!d->toplevel) {
//XXX duplication from applethandle
//create a toplevel view and aim it at the applet.
Corona *corona = d->hostApplet()->containment()->corona();
d->toplevel = new QGraphicsView(corona, 0);
//TODO: use addOffscreenWidget
setParentItem(0);
setPos(-12000, -12000);
d->toplevel->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint
| Qt::WindowStaysOnTopHint);
d->toplevel->resize(screenRect.size());
d->toplevel->setSceneRect(sceneBoundingRect());
d->toplevel->centerOn(this);
//We might have to scale the view, because we might be zoomed out.
qreal scale = screenRect.width() / boundingRect().width();
d->toplevel->scale(scale, scale);
d->toplevel->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
d->toplevel->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
d->toplevel->update();
d->toplevel->show();
}
//move the toplevel view.
d->toplevel->setGeometry(screenRect);
} else {
setParentItem(d->hostApplet());
setPos(d->deltaScene);
//remove the toplevel view.
if (d->toplevel) {
delete d->toplevel;
d->toplevel = 0;
}
}
//let's insert spacers in applets we're hovering over for some usefull visual feedback.
//check which view we're hovering over and use that information to get the mouse
//position in scene coordinates (event->scenePos won't work, since it doesn't take in
//consideration that you're leaving the current view).
QPointF mousePos = d->scenePosFromScreenPos(event->screenPos());
//find the extender we're hovering over.
Extender *targetExtender = 0;
Corona *corona = qobject_cast<Corona*>(scene());
foreach (Containment *containment, corona->containments()) {
foreach (Applet *applet, containment->applets()) {
if (applet->extender() && (applet->sceneBoundingRect().contains(mousePos)
|| applet->extender()->sceneBoundingRect().contains(mousePos))) {
targetExtender = applet->extender();
}
}
}
//remove any previous spacers.
if (targetExtender != d->previousTargetExtender) {
if (d->previousTargetExtender) {
d->previousTargetExtender->itemHoverLeaveEvent(this);
}
d->previousTargetExtender = targetExtender;
if (targetExtender) {
targetExtender->itemHoverEnterEvent(this);
}
}
//insert a spacer if the applet accepts detachables.
if (targetExtender) {
if (targetExtender->sceneBoundingRect().contains(mousePos)) {
targetExtender->itemHoverMoveEvent(this, targetExtender->mapFromScene(mousePos));
}
}
}
void ExtenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
if (d->titleRect().contains(event->pos())) {
if (!d->mouseOver) {
QApplication::setOverrideCursor(Qt::OpenHandCursor);
d->mouseOver = true;
}
} else {
if (d->mouseOver) {
QApplication::restoreOverrideCursor();
d->mouseOver = false;
}
}
}
void ExtenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
if (d->mouseOver) {
QApplication::restoreOverrideCursor();
d->mouseOver = false;
}
}
void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (d->mousePressed) {
d->mousePressed = false;
//remove the toplevel view
if (d->toplevel) {
delete d->toplevel;
d->toplevel = 0;
}
//let's insert spacers in applets we're hovering over for some usefull visual feedback.
//check which view we're hovering over and use that information to get the mouse
//position in scene coordinates (event->scenePos won't work, since it doesn't take in
//consideration that you're leaving the current view).
QPointF mousePos = d->scenePosFromScreenPos(event->screenPos());
//find the extender we're hovering over.
Extender *targetExtender = 0;
Corona *corona = qobject_cast<Corona*>(scene());
foreach (Containment *containment, corona->containments()) {
foreach (Applet *applet, containment->applets()) {
if (applet->extender() && (applet->sceneBoundingRect().contains(mousePos)
|| applet->extender()->sceneBoundingRect().contains(mousePos))) {
targetExtender = applet->extender();
}
}
}
//are we hovering over an applet that accepts extender items?
if (targetExtender) {
if (targetExtender->sceneBoundingRect().contains(mousePos)) {
setExtender(targetExtender, targetExtender->mapFromScene(mousePos));
} else {
setExtender(targetExtender);
}
} else {
//apparently, it is not, so instantiate a new ExtenderApplet.
//TODO: maybe we alow the user to choose a default extenderapplet.
kDebug() << "Instantiate a new ExtenderApplet";
mousePos = d->scenePosFromScreenPos(event->screenPos() - d->mousePos);
foreach (Containment *containment, corona->containments()) {
if (containment->sceneBoundingRect().contains(mousePos)) {
Applet *applet = containment->addApplet("extenderapplet",
QVariantList(),
QRectF(mousePos, size()));
setExtender(applet->extender());
}
}
}
QApplication::restoreOverrideCursor();
}
}
} // namespace Plasma
#include "extenderitem.moc"

213
widgets/extenderitem.h Normal file
View File

@ -0,0 +1,213 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef EXTENDERITEM_H
#define EXTENDERITEM_H
#include <QtGui/QGraphicsWidget>
#include <KDE/KConfigGroup>
#include <KDE/KIcon>
#include "plasma/plasma_export.h"
namespace Plasma
{
class Applet;
class Extender;
class ExtenderItemPrivate;
/**
* This class wraps around a QGraphicsWidget and provides drag&drop handling, a draghandle,
* title and ability to display qactions as a row of icon, ability to expand, collapse, return
* to source and tracks configuration associated with this item for you.
*/
class PLASMA_EXPORT ExtenderItem : public QGraphicsWidget
{
Q_OBJECT
Q_PROPERTY(QGraphicsWidget * widget READ widget WRITE setWidget)
Q_PROPERTY(QString title READ title WRITE setTitle)
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
Q_PROPERTY(Extender * extender READ extender WRITE setExtender)
Q_PROPERTY(bool collapsed READ isCollapsed WRITE setCollapsed)
Q_PROPERTY(bool detached READ isDetached)
Q_PROPERTY(uint autoExpireDelay WRITE setAutoExpireDelay)
public:
/**
* The constructor takes care of adding this item to an extender.
* @param hostExtender The extender where the extender item belongs to.
* TODO: extenderItemId might not be necesarry in the constructor if I rewrite some
* stuff.
* @param extenderItemId the id of the extender item. Use the default 0 to assign a new,
* unique id to this extender item.
*/
ExtenderItem(Extender *hostExtender, uint extenderItemId = 0);
~ExtenderItem();
/**
* fetch the configuration of this widget.
* @return the configuration of this widget.
*/
KConfigGroup config() const;
/**
* @param widget The widget that should be wrapped into the extender item.
*/
void setWidget(QGraphicsWidget *widget);
/**
* @return The widget that is wrapped into the extender item.
*/
QGraphicsWidget *widget() const;
/**
* @param title the title that will be shown in the extender item's dragger. Default is
* no title.
*/
void setTitle(const QString &title);
/**
* @return the title shown in the extender item's dragger.
*/
QString title() const;
/**
* You can assign names to extender items to look them up through the item() function.
* Make sure you only use unique names.
* @param name the name of the item. Defaults to an empty string.
*/
void setName(const QString &name);
/**
* @return the name of the item.
*/
QString name() const;
/**
* @param icon the icon name to display in the extender item's drag handle. Defaults to
* the source applet's icon.
*/
void setIcon(const QString &icon);
/**
* @param icon the icon to display in the extender item's drag handle. Defaults to the
* source applet's icon.
*/
void setIcon(const QIcon &icon);
/**
* @return the icon being displayed in the extender item's drag handle.
*/
QIcon icon() const;
/**
* @param extender the extender this item belongs to.
* @param pos the position in the extender this item should be added. Defaults to 'just
* append'.
*/
void setExtender(Extender *extender, const QPointF &pos = QPointF(-1, -1));
/**
* @return the extender this items belongs to.
*/
Extender *extender() const;
/**
* @param time (in ms) before this extender item destroys itself unless it is detached,
* in which case this extender stays around. 0 means forever and is the default.
*/
void setAutoExpireDelay(uint time = 0);
/**
* @return whether or not this extender item has an auto expire delay.
*/
bool autoExpireDelay() const;
/**
* @return whether or not this item is detached from it's original source.
*/
bool isDetached() const;
/**
* @return whether or not the extender item is collapsed.
*/
bool isCollapsed() const;
/**
* @param name the name to store the action under in our collection.
* @param action the action to add. Actions will be displayed as an icon in the drag
* handle.
*/
void addAction(const QString &name, QAction *action);
/**
* @return the QAction with the given name from our collection. By default the action
* collection contains a "movebacktosource" action which will be only shown when the
* item is detached.
*/
QAction *action(const QString &name) const;
/**
* @return the id of the applet this item is created by.
*/
uint sourceAppletId() const;
public Q_SLOTS:
/**
* Destroys the extender item. As opposed to calling delete on this class, destroy also
* removes the config group associated with this item.
*/
void destroy();
/**
* Collapse or expand the extender item. Defaults to false.
*/
void setCollapsed(bool collapsed);
/**
* Returns the extender item to it's source applet.
*/
void moveBackToSource();
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void resizeEvent(QGraphicsSceneResizeEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
private:
Q_PRIVATE_SLOT(d, void toggleCollapse())
Q_PRIVATE_SLOT(d, void updateToolBox())
ExtenderItemPrivate * const d;
friend class Extender;
};
} // namespace Plasma
#endif // EXTENDERITEM_H

5
widgets/extenderitem_p.h Normal file
View File

@ -0,0 +1,5 @@
#ifndef WIDGETS_EXTENDERITEM_P_H
#define WIDGETS_EXTENDERITEM_P_H
#endif // WIDGETS_EXTENDERITEM_P_H

View File

@ -231,9 +231,11 @@ void Meter::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Dat
{
Q_UNUSED(sourceName)
foreach (const QVariant &d, data) {
if (d.canConvert(QVariant::Int)) {
setValue(d.toInt());
foreach (const QVariant &v, data) {
if (v.type() == QVariant::Int || v.type() == QVariant::UInt
|| v.type() == QVariant::LongLong
|| v.type() == QVariant::ULongLong) {
setValue(v.toInt());
return;
}
}