c676e5130b
svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=864510
939 lines
30 KiB
C++
939 lines
30 KiB
C++
/*
|
|
* Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
|
|
*
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include "extenderitem.h"
|
|
|
|
#include <QApplication>
|
|
#include <QAction>
|
|
#include <QGraphicsSceneResizeEvent>
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <QGraphicsLinearLayout>
|
|
#include <QLayout>
|
|
#include <QPainter>
|
|
#include <QTimer>
|
|
|
|
#include <KDebug>
|
|
#include <KIcon>
|
|
#include <KWindowSystem>
|
|
|
|
#include "applet.h"
|
|
#include "containment.h"
|
|
#include "corona.h"
|
|
#include "dialog.h"
|
|
#include "extender.h"
|
|
#include "panelsvg.h"
|
|
#include "popupapplet.h"
|
|
#include "theme.h"
|
|
#include "view.h"
|
|
|
|
#include "private/applet_p.h"
|
|
#include "private/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;
|
|
}
|
|
|
|
/**
|
|
* @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(),
|
|
dragger->elementSize("hint-preferred-icon-size").height() + top + bottom);
|
|
|
|
return rect;
|
|
}
|
|
|
|
QRectF titleRect()
|
|
{
|
|
qreal left, top, right, bottom;
|
|
dragger->getMargins(left, top, right, bottom);
|
|
return dragHandleRect().adjusted(left + collapseIcon->size().width() + 1, 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)
|
|
{
|
|
if ((q->sceneBoundingRect().left() < 0)) {
|
|
//always leaveCurrentView when the item is in a Plasma::Dialog which can easy be
|
|
//seen by checking if it is in topleft quadrant and it's not just there because it's
|
|
//being viewed by the toplevel view.
|
|
return true;
|
|
}
|
|
|
|
foreach (QWidget *widget, QApplication::topLevelWidgets()) {
|
|
if (widget->geometry().intersects(rect) && widget->isVisible() && widget != toplevel) {
|
|
//is this widget a plasma view, a different view then our current one,
|
|
//AND not a dashboardview?
|
|
QGraphicsView *v = qobject_cast<QGraphicsView*>(widget);
|
|
QGraphicsView *currentV = 0;
|
|
|
|
if (hostApplet()) {
|
|
currentV = qobject_cast<QGraphicsView*>(hostApplet()->containment()->view());
|
|
}
|
|
|
|
if (v && v != currentV) {
|
|
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) {
|
|
//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->resize(minimum);
|
|
toolbox->setPos(q->size().width() - minimum.width(),
|
|
((dragger->size().height() + top + bottom)/2) -
|
|
(minimum.height()/2));
|
|
toolbox->update();
|
|
}
|
|
}
|
|
|
|
//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 = 0;
|
|
|
|
//first check if we're over a Dialog.
|
|
Dialog *dialog = qobject_cast<Dialog*>(w);
|
|
if (dialog) {
|
|
if (dialog->isVisible() && dialog->geometry().contains(pos)) {
|
|
v = qobject_cast<QGraphicsView*>(dialog->layout()->itemAt(0)->widget());
|
|
if (v) {
|
|
return v->mapToScene(v->mapFromGlobal(pos));
|
|
}
|
|
}
|
|
} else {
|
|
v = qobject_cast<QGraphicsView *>(w);
|
|
}
|
|
|
|
//else check if it is a QGV:
|
|
if (v && w->isVisible() && w->geometry().contains(pos)) {
|
|
if (found && order.contains(found->winId())) {
|
|
if (order.indexOf(found->winId()) < order.indexOf(v->winId())) {
|
|
found = v;
|
|
}
|
|
} else {
|
|
found = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
return QPointF();
|
|
}
|
|
|
|
return found->mapToScene(found->mapFromGlobal(pos));
|
|
}
|
|
|
|
Applet *hostApplet() const
|
|
{
|
|
if (extender) {
|
|
return extender->d->applet;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ExtenderItem *q;
|
|
|
|
QGraphicsItem *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(this);
|
|
d->dragger->setImagePath("widgets/dragger");
|
|
|
|
d->appletBackground = new PanelSvg(this);
|
|
d->appletBackground->setImagePath("widgets/background");
|
|
d->appletBackground->setEnabledBorders(0);
|
|
|
|
//create the toolbox.
|
|
d->toolbox = new QGraphicsWidget(this);
|
|
d->toolboxLayout = new QGraphicsLinearLayout(d->toolbox);
|
|
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 = d->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(QGraphicsItem *widget)
|
|
{
|
|
qreal left, top, right, bottom;
|
|
d->dragger->getMargins(left, top, right, bottom);
|
|
widget->setParentItem(this);
|
|
widget->setPos(QPointF(left, d->dragHandleRect().height() + bottom));
|
|
d->widget = widget;
|
|
setCollapsed(isCollapsed()); //updates the size hints.
|
|
}
|
|
|
|
QGraphicsItem *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)
|
|
{
|
|
Q_ASSERT(extender);
|
|
|
|
if (extender == d->extender) {
|
|
//We're not moving between extenders, so just insert this item back into the layout.
|
|
setParentItem(extender);
|
|
extender->d->addExtenderItem(this, pos);
|
|
return;
|
|
}
|
|
|
|
//We are switching extender...
|
|
//first remove this item from the old extender.
|
|
d->extender->d->removeExtenderItem(this);
|
|
emit d->extender->itemDetached(this);
|
|
|
|
//collapse the popupapplet if the last item is removed.
|
|
if (!d->extender->attachedItems().count()) {
|
|
PopupApplet *applet = qobject_cast<PopupApplet*>(d->extender->d->applet);
|
|
if (applet) {
|
|
applet->hidePopup();
|
|
}
|
|
}
|
|
|
|
//move the configuration.
|
|
if (d->hostApplet() && (extender != d->extender)) {
|
|
KConfigGroup c = extender->d->applet->config("ExtenderItems");
|
|
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 (!time) {
|
|
if (d->expirationTimer) {
|
|
d->expirationTimer->stop();
|
|
delete d->expirationTimer;
|
|
d->expirationTimer = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!isDetached()) {
|
|
if (!d->expirationTimer) {
|
|
d->expirationTimer = new QTimer(this);
|
|
connect(d->expirationTimer, SIGNAL(timeout()), this, SLOT(destroy()));
|
|
}
|
|
|
|
d->expirationTimer->stop();
|
|
d->expirationTimer->setSingleShot(true);
|
|
d->expirationTimer->setInterval(time);
|
|
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()
|
|
{
|
|
if (d->mousePressed) {
|
|
//avoid being destroyed while we're being dragged.
|
|
return;
|
|
}
|
|
|
|
d->hostApplet()->config("ExtenderItems").deleteGroup(QString::number(d->extenderItemId));
|
|
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()));
|
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
updateGeometry();
|
|
return;
|
|
}
|
|
|
|
qreal left, top, right, bottom;
|
|
d->dragger->getMargins(left, top, right, bottom);
|
|
|
|
d->widget->setVisible(!collapsed);
|
|
|
|
QSizeF minimumSize;
|
|
QSizeF preferredSize;
|
|
QSizeF maximumSize;
|
|
|
|
if (d->widget->isWidget()) {
|
|
QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(d->widget);
|
|
minimumSize = graphicsWidget->minimumSize();
|
|
preferredSize = graphicsWidget->preferredSize();
|
|
maximumSize = graphicsWidget->maximumSize();
|
|
} else {
|
|
minimumSize = d->widget->boundingRect().size();
|
|
preferredSize = d->widget->boundingRect().size();
|
|
maximumSize = d->widget->boundingRect().size();
|
|
}
|
|
|
|
if (collapsed) {
|
|
setPreferredSize(QSizeF(preferredSize.width() + left + right,
|
|
d->dragHandleRect().height()));
|
|
setMinimumSize(QSizeF(minimumSize.width() + left + right,
|
|
d->dragHandleRect().height()));
|
|
setMaximumSize(QSizeF(maximumSize.width() + left + right,
|
|
d->dragHandleRect().height()));
|
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
//FIXME: why don't tooltips work?
|
|
if (d->collapseIcon) {
|
|
d->collapseIcon->setToolTip(i18n("Expand this widget"));
|
|
}
|
|
} else {
|
|
setPreferredSize(QSizeF(preferredSize.width() + left + right,
|
|
preferredSize.height() +
|
|
d->dragHandleRect().height() + top + bottom));
|
|
setMinimumSize( QSizeF(minimumSize.width() + left + right,
|
|
minimumSize.height() +
|
|
d->dragHandleRect().height() + top + bottom));
|
|
setMaximumSize( QSizeF(maximumSize.width() + left + right,
|
|
maximumSize.height() +
|
|
d->dragHandleRect().height() + top + bottom));
|
|
|
|
if (d->widget->isWidget()) {
|
|
QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(d->widget);
|
|
setSizePolicy(graphicsWidget->sizePolicy());
|
|
} else {
|
|
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
}
|
|
|
|
if (d->collapseIcon) {
|
|
d->collapseIcon->setToolTip(i18n("Collapse this widget"));
|
|
}
|
|
}
|
|
|
|
updateGeometry();
|
|
|
|
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);
|
|
font.setWeight(QFont::Bold);
|
|
|
|
//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 && d->widget->isWidget()) {
|
|
QSizeF newWidgetSize = event->newSize();
|
|
newWidgetSize.setHeight(newWidgetSize.height() - d->dragger->size().height() - top - bottom);
|
|
newWidgetSize.setWidth(newWidgetSize.width() - left - right);
|
|
|
|
QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(d->widget);
|
|
graphicsWidget->resize(newWidgetSize);
|
|
}
|
|
|
|
d->updateToolBox();
|
|
}
|
|
|
|
void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if (!(d->dragHandleRect().contains(event->pos()))) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
d->mousePressed = true;
|
|
d->deltaScene = pos();
|
|
|
|
Applet *parentApplet = d->hostApplet();
|
|
|
|
d->mousePos = event->pos().toPoint();
|
|
|
|
parentApplet->raise();
|
|
setZValue(parentApplet->zValue());
|
|
|
|
QPointF mousePos = d->scenePosFromScreenPos(event->screenPos());
|
|
|
|
if (!mousePos.isNull()) {
|
|
d->extender->itemHoverMoveEvent(this, d->extender->mapFromScene(mousePos));
|
|
}
|
|
|
|
d->extender->d->removeExtenderItem(this);
|
|
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
|
|
}
|
|
|
|
void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *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());
|
|
|
|
Corona *corona = d->hostApplet()->containment()->corona();
|
|
|
|
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) {
|
|
//FIXME: duplication from applethandle
|
|
//create a toplevel view and aim it at the applet.
|
|
d->toplevel = new QGraphicsView(corona, 0);
|
|
|
|
corona->addOffscreenWidget(this);
|
|
|
|
d->toplevel->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint
|
|
| Qt::WindowStaysOnTopHint);
|
|
d->toplevel->setFrameShape(QFrame::NoFrame);
|
|
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->setSceneRect(sceneBoundingRect());
|
|
d->toplevel->setGeometry(screenRect);
|
|
update();
|
|
} else {
|
|
corona->removeOffscreenWidget(this);
|
|
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;
|
|
|
|
if (!mousePos.isNull()) {
|
|
foreach (Containment *containment, corona->containments()) {
|
|
foreach (Applet *applet, containment->applets()) {
|
|
if (applet->d->extender &&
|
|
(applet->sceneBoundingRect().contains(mousePos) ||
|
|
applet->d->extender->sceneBoundingRect().contains(mousePos))) {
|
|
targetExtender = applet->d->extender;
|
|
|
|
//check if we're hovering over an popupapplet, and open it up in case it does.
|
|
PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet);
|
|
if (popupApplet && (applet->formFactor() == Plasma::Horizontal ||
|
|
applet->formFactor() == Plasma::Vertical)) {
|
|
popupApplet->showPopup();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//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());
|
|
|
|
corona->removeOffscreenWidget(this);
|
|
|
|
if (!mousePos.isNull()) {
|
|
foreach (Containment *containment, corona->containments()) {
|
|
foreach (Applet *applet, containment->applets()) {
|
|
if (applet->d->extender &&
|
|
(applet->sceneBoundingRect().contains(mousePos) ||
|
|
applet->d->extender->sceneBoundingRect().contains(mousePos))) {
|
|
targetExtender = applet->d->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);
|
|
if (!mousePos.isNull()) {
|
|
foreach (Containment *containment, corona->containments()) {
|
|
if (containment->sceneBoundingRect().contains(mousePos)) {
|
|
Applet *applet = containment->addApplet("internal:extender",
|
|
QVariantList(),
|
|
QRectF(mousePos, size()));
|
|
setExtender(applet->d->extender);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
}
|
|
|
|
} // namespace Plasma
|
|
|
|
#include "extenderitem.moc"
|