plasma-framework/popupapplet.cpp
Marco Martin 604aef6bd1 based upon a patch by aaron,
now if an empty icon is set the popup is always shown also in the
desktop and the applet area painting has to be done by the
implementation
first using it also in this patch is libplasmaclock
also, api change:
pupApplet::setIcon and icon() became setPopupIcon() and popupIcon()


svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=860338
2008-09-12 18:32:01 +00:00

493 lines
14 KiB
C++

/*
* Copyright 2008 by Montel Laurent <montel@kde.org>
*
* 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 "popupapplet.h"
#include <QGraphicsProxyWidget>
#include <QGraphicsLinearLayout>
#include <QVBoxLayout>
#include <QTimer>
#include <QApplication>
#include <QDesktopWidget>
#include <KIcon>
#include <KIconLoader>
#include <KWindowSystem>
#include <KGlobalSettings>
#include <plasma/dialog.h>
#include <plasma/corona.h>
#include <plasma/containment.h>
#include <plasma/extender.h>
#include <plasma/widgets/icon.h>
namespace Plasma
{
class PopupAppletPrivate
{
public:
PopupAppletPrivate(PopupApplet *applet)
: q(applet),
icon(0),
dialog(0),
layout(0),
proxy(0),
popupPlacement(Plasma::FloatingPopup),
savedAspectRatio(Plasma::InvalidAspectRatioMode),
timer(0),
startupComplete(false)
{
}
~PopupAppletPrivate()
{
if (proxy) {
proxy->setWidget(0);
}
delete dialog;
delete icon;
}
void togglePopup();
void hideTimedPopup();
void dialogSizeChanged();
void dialogStatusChanged(bool status);
void updateDialogPosition();
PopupApplet *q;
Plasma::Icon *icon;
Plasma::Dialog *dialog;
QGraphicsLinearLayout *layout;
QGraphicsProxyWidget *proxy;
Plasma::PopupPlacement popupPlacement;
Plasma::AspectRatioMode savedAspectRatio;
QTimer *timer;
QPoint clicked;
bool startupComplete;
};
PopupApplet::PopupApplet(QObject *parent, const QVariantList &args)
: Plasma::Applet(parent, args),
d(new PopupAppletPrivate(this))
{
int iconSize = IconSize(KIconLoader::Desktop);
resize(iconSize, iconSize);
if (!icon().isNull()) {
setPopupIcon(KIcon(icon()));
} else {
setPopupIcon(KIcon("icons"));
}
}
PopupApplet::~PopupApplet()
{
delete d;
}
void PopupApplet::setPopupIcon(const QIcon &icon)
{
if (icon.isNull()) {
if (d->icon) {
delete d->icon;
d->icon = 0;
setLayout(0);
}
return;
}
if (!d->icon) {
d->icon = new Plasma::Icon(icon, QString(), this);
connect(d->icon, SIGNAL(clicked()), this, SLOT(togglePopup()));
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->setOrientation(Qt::Horizontal);
setAspectRatioMode(Plasma::ConstrainedSquare);
setLayout(layout);
} else {
d->icon->setIcon(icon);
}
}
void PopupApplet::setPopupIcon(const QString &iconName)
{
setPopupIcon(KIcon(iconName));
}
QIcon PopupApplet::popupIcon() const
{
return d->icon ? d->icon->icon() : QIcon();
}
QWidget *PopupApplet::widget()
{
return 0;
}
QGraphicsWidget *PopupApplet::graphicsWidget()
{
return 0;
}
void PopupApplet::constraintsEvent(Plasma::Constraints constraints)
{
if (constraints & Plasma::StartupCompletedConstraint) {
d->startupComplete = true;
}
if (!d->startupComplete) {
return;
}
QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(layout());
if (constraints & Plasma::FormFactorConstraint) {
if (lay) {
lay->removeAt(0);
}
if ((formFactor() != Plasma::Vertical && formFactor() != Plasma::Horizontal) &&
d->icon) {
// we only switch to expanded if we aren't horiz/vert constrained and
// this applet has an icon.
// otherwise, we leave it up to the applet itself to figure it out
d->icon->hide();
if (d->savedAspectRatio != Plasma::InvalidAspectRatioMode) {
setAspectRatioMode(d->savedAspectRatio);
}
if (d->dialog) {
if (d->dialog->layout() && widget()) {
//we dont want to delete Widget inside the dialog layout
d->dialog->layout()->removeWidget(widget());
}
delete d->dialog;
d->dialog = 0;
}
//get the margins
QSizeF marginSize = size() - contentsRect().size();
QGraphicsWidget *gWidget = graphicsWidget();
if (gWidget) {
if (lay) {
lay->addItem(gWidget);
}
setMinimumSize(gWidget->minimumSize() + marginSize);
gWidget->installEventFilter(this);
} else if (widget()) {
if (!d->proxy) {
d->proxy = new QGraphicsProxyWidget(this);
d->proxy->setWidget(widget());
d->proxy->show();
}
if (lay) {
lay->addItem(d->proxy);
}
setMinimumSize(widget() ? widget()->minimumSize() + marginSize : QSizeF(300, 200));
}
} else {
//save the aspect ratio mode in case we drag'n drop in the Desktop later
d->savedAspectRatio = aspectRatioMode();
setAspectRatioMode(Plasma::ConstrainedSquare);
if (d->icon) {
d->icon->show();
}
if (d->proxy) {
d->proxy->setWidget(0); // prevent it from deleting our widget!
delete d->proxy;
d->proxy = 0;
}
if (!d->dialog) {
kDebug() << "making dialog with view" << view();
d->dialog = new Plasma::Dialog();
//no longer use Qt::Popup since that seems to cause a lot of problem when you drag
//stuff out of your Dialog (extenders). Monitor WindowDeactivate events so we can
//emulate the same kind of behavior as Qt::Popup (close when you click somewhere
//else.
d->dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
KWindowSystem::setState(d->dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
d->dialog->installEventFilter(this);
connect(d->dialog, SIGNAL(dialogResized()), this, SLOT(dialogSizeChanged()));
connect(d->dialog, SIGNAL(dialogVisible(bool)), this , SLOT(dialogStatusChanged(bool)));
QGraphicsWidget *gWidget = graphicsWidget();
if (gWidget) {
Corona *corona = qobject_cast<Corona *>(gWidget->scene());
//could that cast ever fail??
if (corona) {
corona->addOffscreenWidget(gWidget);
graphicsWidget()->resize(gWidget->preferredSize());
graphicsWidget()->setMinimumSize(gWidget->preferredSize());
d->dialog->setGraphicsWidget(gWidget);
}
} else if (widget()) {
QVBoxLayout *l_layout = new QVBoxLayout(d->dialog);
l_layout->setSpacing(0);
l_layout->setMargin(0);
l_layout->addWidget(widget());
}
}
d->dialog->adjustSize();
if (d->icon && lay) {
lay->addItem(d->icon);
}
}
}
}
void PopupApplet::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (!d->icon && event->buttons() == Qt::LeftButton) {
d->clicked = scenePos().toPoint();
event->setAccepted(true);
return;
} else {
Applet::mousePressEvent(event);
}
}
void PopupApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (!d->icon && (d->clicked - scenePos().toPoint()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
d->togglePopup();
} else {
Applet::mouseReleaseEvent(event);
}
}
bool PopupApplet::eventFilter(QObject *watched, QEvent *event)
{
if (watched == d->dialog && (event->type() == QEvent::WindowDeactivate)) {
hidePopup();
}
if (watched == graphicsWidget() && (event->type() == QEvent::GraphicsSceneResize)) {
//sizes are recalculated in the constraintsevent so let's just call that.
constraintsEvent(Plasma::FormFactorConstraint);
//resize vertically if necesarry.
if (formFactor() == Plasma::MediaCenter || formFactor() == Plasma::Planar) {
resize(QSizeF(size().width(), minimumHeight()));
}
}
return Applet::eventFilter(watched, event);
}
QVariant PopupApplet::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemParentHasChanged && d->dialog) {
d->dialog->setParent(view());
}
return Applet::itemChange(change, value);
}
void PopupApplet::showPopup(uint popupDuration)
{
if (d->dialog && (formFactor() == Horizontal || formFactor() == Vertical)) {
d->updateDialogPosition();
d->dialog->show();
KWindowSystem::setState(d->dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
if (d->timer) {
d->timer->stop();
}
if (popupDuration > 0) {
if (!d->timer) {
d->timer = new QTimer(this);
connect(d->timer, SIGNAL(timeout()), this, SLOT(hideTimedPopup()));
}
d->timer->start(popupDuration);
}
}
}
void PopupApplet::hidePopup()
{
if (d->dialog && (formFactor() == Horizontal || formFactor() == Vertical)) {
d->dialog->hide();
}
}
Plasma::PopupPlacement PopupApplet::popupPlacement() const
{
return d->popupPlacement;
}
void PopupApplet::popupEvent(bool)
{
}
void PopupAppletPrivate::togglePopup()
{
if (dialog) {
if (timer) {
timer->stop();
}
if (dialog->isVisible()) {
dialog->hide();
} else {
updateDialogPosition();
dialog->show();
KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
}
dialog->clearFocus();
}
}
void PopupAppletPrivate::hideTimedPopup()
{
timer->stop();
q->hidePopup();
}
void PopupAppletPrivate::dialogSizeChanged()
{
//Reposition the dialog
if (dialog) {
dialog->updateGeometry();
dialog->move(q->popupPosition(dialog->size()));
KConfigGroup sizeGroup = KConfigGroup(&q->config(), "PopupApplet");
sizeGroup.writeEntry("DialogHeight", dialog->height());
sizeGroup.writeEntry("DialogWidth", dialog->width());
emit q->configNeedsSaving();
}
}
void PopupAppletPrivate::dialogStatusChanged(bool status)
{
q->popupEvent(status);
}
void PopupAppletPrivate::updateDialogPosition()
{
KConfigGroup sizeGroup = KConfigGroup(&q->config(), "PopupApplet");
const int width = qMin(sizeGroup.readEntry("DialogWidth", 0), QApplication::desktop()->screen()->width() - 50);
const int height = qMin(sizeGroup.readEntry("DialogHeight", 0), QApplication::desktop()->screen()->height() - 50);
QSize saved(width, height);
if (saved.isNull()) {
dialog->adjustSize();
} else {
saved = saved.expandedTo(dialog->minimumSizeHint());
dialog->resize(saved);
}
QSize s = dialog->size();
QPoint pos = q->view()->mapFromScene(q->scenePos());
pos = q->view()->mapToGlobal(pos);
switch (q->location()) {
case BottomEdge:
pos = QPoint(pos.x(), pos.y() - s.height());
popupPlacement = Plasma::TopPosedLeftAlignedPopup;
dialog->setResizeHandleCorners(Dialog::NorthEast);
break;
case TopEdge:
pos = QPoint(pos.x(), pos.y() + (int)q->boundingRect().size().height());
popupPlacement = Plasma::BottomPosedLeftAlignedPopup;
dialog->setResizeHandleCorners(Dialog::SouthEast);
break;
case LeftEdge:
pos = QPoint(pos.x() + (int)q->boundingRect().size().width(), pos.y());
popupPlacement = Plasma::RightPosedTopAlignedPopup;
dialog->setResizeHandleCorners(Dialog::SouthEast);
break;
case RightEdge:
pos = QPoint(pos.x() - s.width(), pos.y());
popupPlacement = Plasma::LeftPosedTopAlignedPopup;
dialog->setResizeHandleCorners(Dialog::SouthWest);
break;
default:
if (pos.y() - s.height() > 0) {
pos = QPoint(pos.x(), pos.y() - s.height());
} else {
pos = QPoint(pos.x(), pos.y() + (int)q->boundingRect().size().height());
}
dialog->setResizeHandleCorners(Dialog::NorthEast);
}
//are we out of screen?
QRect screenRect = QApplication::desktop()->screenGeometry(q->containment() ? q->containment()->screen() : -1);
//kDebug() << "==> rect for" << (containment() ? containment()->screen() : -1) << "is" << screenRect;
if (pos.rx() + s.width() > screenRect.right()) {
pos.rx() += (int)q->boundingRect().size().width() - s.width();
if (q->location() == BottomEdge) {
popupPlacement = Plasma::TopPosedRightAlignedPopup;
dialog->setResizeHandleCorners(Dialog::NorthWest);
} else if (q->location() == TopEdge) {
popupPlacement = Plasma::BottomPosedRightAlignedPopup;
dialog->setResizeHandleCorners(Dialog::SouthWest);
}
}
if (pos.ry() + s.height() > screenRect.bottom()) {
pos.ry() += (int)q->boundingRect().size().height() - s.height();
if (q->location() == LeftEdge) {
popupPlacement = Plasma::RightPosedBottomAlignedPopup;
dialog->setResizeHandleCorners(Dialog::NorthEast);
} else if (q->location() == RightEdge) {
popupPlacement = Plasma::LeftPosedBottomAlignedPopup;
dialog->setResizeHandleCorners(Dialog::NorthWest);
}
}
pos.rx() = qMax(0, pos.rx());
dialog->move(pos);
}
} // Plasma namespace
#include "popupapplet.moc"