plasma-framework/graphicsview/private/graphicsviewappletprivate.cpp
David Faure af211f7c93 KIcon("foo") -> KDE::icon("foo")
include kicon.h -> kiconloader.h, or often, nothing at all.
2012-04-28 13:17:42 +02:00

550 lines
18 KiB
C++

/*
* Copyright 2011 by Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library 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 "graphicsviewappletprivate_p.h"
#include <QGraphicsLinearLayout>
#include <QGraphicsGridLayout>
#include "kwindowsystem.h"
#include "corona.h"
#include "dialog.h"
#include "paintutils.h"
#include "popupapplet.h"
#include "theme.h"
#include "tooltipmanager.h"
#include "widgets/busywidget.h"
#include "widgets/iconwidget.h"
#include "widgets/label.h"
#include "widgets/pushbutton.h"
namespace Plasma
{
GraphicsViewAppletPrivate::GraphicsViewAppletPrivate(KService::Ptr service, const KPluginInfo *info, int uniqueID, Applet *applet)
: AppletPrivate(service, info, uniqueID, applet),
messageOverlay(0),
messageOverlayProxy(0),
busyWidget(0)
{
q->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
q->setAcceptsHoverEvents(true);
q->setFlag(QGraphicsItem::ItemIsFocusable, true);
q->setFocusPolicy(Qt::ClickFocus);
// FIXME: adding here because nothing seems to be doing it in QGraphicsView,
// but it doesn't actually work anyways =/
q->setLayoutDirection(qApp->layoutDirection());
}
void GraphicsViewAppletPrivate::showMessage(const QIcon &icon, const QString &message, const MessageButtons buttons)
{
if (message.isEmpty()) {
destroyMessageOverlay();
return;
}
Corona *corona = qobject_cast<Corona *>(q->scene());
QGraphicsWidget *mainWidget = new QGraphicsWidget;
QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(mainWidget);
mainLayout->setOrientation(Qt::Vertical);
mainLayout->addStretch();
QGraphicsLinearLayout *messageLayout = new QGraphicsLinearLayout();
messageLayout->setOrientation(Qt::Horizontal);
QGraphicsLinearLayout *buttonLayout = new QGraphicsLinearLayout();
buttonLayout->setOrientation(Qt::Horizontal);
mainLayout->addItem(messageLayout);
mainLayout->addItem(buttonLayout);
mainLayout->addStretch();
IconWidget *messageIcon = new IconWidget(mainWidget);
Label *messageText = new Label(mainWidget);
messageText->nativeWidget()->setWordWrap(true);
messageLayout->addStretch();
messageLayout->addItem(messageIcon);
messageLayout->addItem(messageText);
messageLayout->addStretch();
messageIcon->setIcon(icon);
messageText->setText(message);
buttonLayout->addStretch();
if (buttons & ButtonOk) {
messageOkButton = new PushButton(mainWidget);
messageOkButton.data()->setText(i18n("&OK"));
messageOkButton.data()->setIcon(KDE::icon("dialog-ok"));
buttonLayout->addItem(messageOkButton.data());
QObject::connect(messageOkButton.data(), SIGNAL(clicked()), q, SLOT(destroyMessageOverlay()));
}
if (buttons & ButtonYes) {
messageYesButton = new PushButton(mainWidget);
messageYesButton.data()->setText(i18n("&Yes"));
buttonLayout->addItem(messageYesButton.data());
QObject::connect(messageYesButton.data(), SIGNAL(clicked()), q, SLOT(destroyMessageOverlay()));
}
if (buttons & ButtonNo) {
messageNoButton = new PushButton(mainWidget);
messageNoButton.data()->setText(i18n("&No"));
buttonLayout->addItem(messageNoButton.data());
QObject::connect(messageNoButton.data(), SIGNAL(clicked()), q, SLOT(destroyMessageOverlay()));
}
if (buttons & ButtonCancel) {
messageCancelButton = new PushButton(mainWidget);
messageCancelButton.data()->setText(i18n("&Cancel"));
messageCancelButton.data()->setIcon(KDE::icon("dialog-cancel"));
buttonLayout->addItem(messageCancelButton.data());
QObject::connect(messageCancelButton.data(), SIGNAL(clicked()), q, SLOT(destroyMessageOverlay()));
}
messageCloseAction = new QAction(messageOverlay);
messageCloseAction.data()->setShortcut(Qt::Key_Escape);
mainWidget->addAction(messageCloseAction.data());
QObject::connect(messageCloseAction.data(), SIGNAL(triggered()), q, SLOT(destroyMessageOverlay()));
buttonLayout->addStretch();
mainWidget->adjustSize();
QSizeF hint = mainWidget->preferredSize();
if (hint.height() > q->size().height() || hint.width() > q->size().width()) {
// either a collapsed popup in h/v form factor or just too small,
// so show it in a dialog associated with ourselves
if (corona) {
corona->addOffscreenWidget(mainWidget);
}
if (messageDialog) {
delete messageDialog.data()->graphicsWidget();
} else {
messageDialog = new Plasma::Dialog;
}
ToolTipManager::self()->hide(q);
KWindowSystem::setOnAllDesktops(messageDialog.data()->winId(), true);
KWindowSystem::setState(messageDialog.data()->winId(), NET::SkipTaskbar | NET::SkipPager);
messageDialog.data()->setGraphicsWidget(mainWidget);
QObject::connect(messageDialog.data(), SIGNAL(destroyed(QObject*)), mainWidget, SLOT(deleteLater()));
// if we are going to show it in a popup, then at least make sure it can be dismissed
if (buttonLayout->count() < 1) {
PushButton *ok = new PushButton(mainWidget);
ok->setText(i18n("OK"));
ok->setIcon(KDE::icon("dialog-ok"));
buttonLayout->addItem(ok);
QObject::connect(ok, SIGNAL(clicked()), q, SLOT(destroyMessageOverlay()));
}
} else {
delete messageDialog.data();
createMessageOverlay();
messageOverlay->opacity = 0.8;
mainWidget->setParentItem(messageOverlay);
QGraphicsLinearLayout *l = new QGraphicsLinearLayout(messageOverlay);
l->addItem(mainWidget);
}
if (messageDialog) {
QPoint pos = q->geometry().topLeft().toPoint();
if (corona) {
pos = corona->popupPosition(q, messageDialog.data()->size());
}
messageDialog.data()->move(pos);
messageDialog.data()->animatedShow(locationToDirection(q->location()));
} else {
messageOverlay->show();
}
}
void GraphicsViewAppletPrivate::updateFailedToLaunch(const QString &reason)
{
if (failed == failed) {
if (failed && !reason.isEmpty()) {
foreach (QGraphicsItem *item, q->QGraphicsItem::children()) {
Label *l = dynamic_cast<Label *>(item);
if (l) {
l->setText(visibleFailureText(reason));
}
}
}
return;
}
//FIXME: q->prepareGeometryChange();
foreach (QGraphicsItem *item, q->childItems()) {
if (!dynamic_cast<AppletHandle *>(item)) {
delete item;
}
}
messageOverlay = 0;
if (messageDialog) {
messageDialog.data()->deleteLater();
messageDialog.clear();
}
q->setLayout(0);
if (failed) {
q->setBackgroundHints(StandardBackground);
QGraphicsLinearLayout *failureLayout = new QGraphicsLinearLayout(q);
failureLayout->setContentsMargins(0, 0, 0, 0);
IconWidget *failureIcon = new IconWidget(q);
failureIcon->setIcon(KDE::icon("dialog-error"));
failureLayout->addItem(failureIcon);
Label *failureWidget = new Plasma::Label(q);
failureWidget->setText(visibleFailureText(reason));
QLabel *label = failureWidget->nativeWidget();
label->setWordWrap(true);
failureLayout->addItem(failureWidget);
Plasma::ToolTipManager::self()->registerWidget(failureIcon);
Plasma::ToolTipContent data(i18n("Unable to load the widget"), reason,
KDE::icon("dialog-error"));
Plasma::ToolTipManager::self()->setContent(failureIcon, data);
q->setLayout(failureLayout);
q->resize(300, 250);
background->resizeFrame(q->geometry().size());
}
q->update();
}
void GraphicsViewAppletPrivate::cleanUpAndDelete()
{
//kDebug() << "???????????????? DESTROYING APPLET" << q->name() << q->scene() << " ???????????????????????????";
QGraphicsWidget *parent = dynamic_cast<QGraphicsWidget *>(q->parentItem());
//it probably won't matter, but right now if there are applethandles, *they* are the parent.
//not the containment.
//is the applet in a containment and does the containment have a layout?
//if yes, we remove the applet in the layout
if (parent && parent->layout()) {
QGraphicsLayout *l = parent->layout();
for (int i = 0; i < l->count(); ++i) {
if (q == l->itemAt(i)) {
l->removeAt(i);
break;
}
}
}
if (configLoader) {
configLoader->setDefaults();
}
resetConfigurationObject();
if (q->scene()) {
if (isContainment) {
// prematurely emit our destruction if we are a Containment,
// giving Corona a chance to remove this Containment from its collection
emit q->QObject::destroyed(q);
}
q->scene()->removeItem(q);
}
q->deleteLater();
}
void GraphicsViewAppletPrivate::showConfigurationRequiredMessage(bool show, const QString &reason)
{
if (!show) {
destroyMessageOverlay();
return;
}
createMessageOverlay(true);
messageOverlay->opacity = 0.4;
QGraphicsGridLayout *configLayout = new QGraphicsGridLayout(messageOverlay);
configLayout->setContentsMargins(0, 0, 0, 0);
// configLayout->addStretch();
configLayout->setColumnStretchFactor(0, 5);
configLayout->setColumnStretchFactor(2, 5);
configLayout->setRowStretchFactor(0, 5);
configLayout->setRowStretchFactor(3, 5);
int row = 1;
if (!reason.isEmpty()) {
Label *explanation = new Label(messageOverlay);
explanation->setText(reason);
configLayout->addItem(explanation, row, 1);
configLayout->setColumnStretchFactor(1, 5);
++row;
configLayout->setAlignment(explanation, Qt::AlignBottom | Qt::AlignCenter);
}
PushButton *configWidget = new PushButton(messageOverlay);
QObject::connect(configWidget, SIGNAL(clicked()), q, SLOT(showConfigurationInterface()));
const FormFactor formFactor = q->formFactor();
if (!qobject_cast<Plasma::PopupApplet *>(q) && (formFactor == Plasma::Horizontal || formFactor == Plasma::Vertical)) {
configWidget->setImage("widgets/configuration-icons", "configure");
configWidget->setMaximumSize(24,24);
configWidget->setMinimumSize(24,24);
} else {
configWidget->setText(i18n("Configure..."));
}
configLayout->addItem(configWidget, row, 1);
//configLayout->setAlignment(configWidget, Qt::AlignTop | Qt::AlignCenter);
//configLayout->addStretch();
messageOverlay->show();
}
void GraphicsViewAppletPrivate::createMessageOverlay(bool usePopup)
{
if (messageOverlay) {
qDeleteAll(messageOverlay->children());
messageOverlay->setLayout(0);
}
PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q);
if (!messageOverlay) {
if (usePopup && popup) {
if (popup->widget()) {
messageOverlayProxy = new QGraphicsProxyWidget(q);
messageOverlayProxy->setWidget(popup->widget());
messageOverlay = new AppletOverlayWidget(messageOverlayProxy);
} else if (popup->graphicsWidget()) {
messageOverlay = new AppletOverlayWidget(popup->graphicsWidget());
}
}
if (!messageOverlay) {
messageOverlay = new AppletOverlayWidget(q);
}
}
positionMessageOverlay();
}
void GraphicsViewAppletPrivate::positionMessageOverlay()
{
if (!messageOverlay) {
return;
}
PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q);
const bool usePopup = popup && (messageOverlay->parentItem() != q);
QGraphicsItem *topItem = q;
if (usePopup && popup->widget()) {
// popupapplet with widget()
//FIXME:topItem = popup->d->proxy.data();
messageOverlay->setGeometry(popup->widget()->contentsRect());
} else if (usePopup && popup->graphicsWidget()) {
// popupapplet with graphicsWidget()
topItem = popup->graphicsWidget();
QGraphicsWidget *w = dynamic_cast<QGraphicsWidget *>(topItem);
messageOverlay->setGeometry(w ? w->contentsRect() : topItem->boundingRect());
} else {
// normal applet
messageOverlay->setGeometry(q->contentsRect());
}
// raise the overlay above all the other children!
int zValue = 100;
foreach (QGraphicsItem *child, topItem->children()) {
if (child->zValue() > zValue) {
zValue = child->zValue() + 1;
}
}
messageOverlay->setZValue(zValue);
}
void GraphicsViewAppletPrivate::destroyMessageOverlay()
{
if (messageDialog) {
messageDialog.data()->animatedHide(Plasma::locationToInverseDirection(q->location()));
//messageDialog.data()->deleteLater();
messageDialog.clear();
}
if (!messageOverlay) {
return;
}
messageOverlay->destroy();
messageOverlay = 0;
if (messageOverlayProxy) {
messageOverlayProxy->setWidget(0);
delete messageOverlayProxy;
messageOverlayProxy = 0;
}
MessageButton buttonCode = ButtonNo;
//find out if we're disappearing because of a button press
PushButton *button = 0;
//FIXME: PushButton *button = qobject_cast<PushButton *>(q->sender());
if (button) {
if (button == messageOkButton.data()) {
buttonCode = ButtonOk;
}
if (button == messageYesButton.data()) {
buttonCode = ButtonYes;
}
if (button == messageNoButton.data()) {
buttonCode = ButtonNo;
}
if (button == messageCancelButton.data()) {
buttonCode = ButtonCancel;
}
//FIXME: emit q->messageButtonPressed(buttonCode);
} else if (/* FIXME: q->sender() == messageOverlay */ false) {
//FIXME: emit q->messageButtonPressed(ButtonCancel);
}
}
void GraphicsViewAppletPrivate::setBusy(bool busy)
{
if (busy) {
if (!busyWidget && !busyWidgetTimer.isActive()) {
busyWidgetTimer.start(500, this);
}
} else {
busyWidgetTimer.stop();
if (busyWidget) {
busyWidget = 0;
destroyMessageOverlay();
}
}
}
bool GraphicsViewAppletPrivate::isBusy() const
{
return busyWidgetTimer.isActive() || (busyWidget && busyWidget->isVisible());
}
void GraphicsViewAppletPrivate::timerEvent(QTimerEvent *event)
{
if (transient) {
busyWidgetTimer.stop();
return;
}
if (event->timerId() == busyWidgetTimer.timerId()) {
if (!busyWidget) {
createMessageOverlay(false);
messageOverlay->opacity = 0;
QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(messageOverlay);
busyWidget = new Plasma::BusyWidget(messageOverlay);
busyWidget->setAcceptHoverEvents(false);
busyWidget->setAcceptedMouseButtons(Qt::NoButton);
messageOverlay->setAcceptHoverEvents(false);
messageOverlay->setAcceptedMouseButtons(Qt::NoButton);
mainLayout->addStretch();
mainLayout->addItem(busyWidget);
mainLayout->addStretch();
}
}
}
AppletOverlayWidget::AppletOverlayWidget(QGraphicsWidget *parent)
: QGraphicsWidget(parent),
opacity(0.4)
{
resize(parent->size());
}
void AppletOverlayWidget::destroy()
{
/*FIXME: a transitional animation would be nice here .. we used to do this:
Animation *anim = Plasma::Animator::create(Plasma::Animator::DisappearAnimation);
if (anim) {
connect(anim, SIGNAL(finished()), this, SLOT(overlayAnimationComplete()));
anim->setTargetWidget(this);
anim->start();
} else {
overlayAnimationComplete();
}
*/
if (scene()) {
scene()->removeItem(this);
}
deleteLater();
}
void AppletOverlayWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
event->accept();
}
void AppletOverlayWidget::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
if (qFuzzyCompare(1, 1+opacity)) {
return;
}
QColor wash = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor);
wash.setAlphaF(opacity);
Applet *applet = qobject_cast<Applet *>(parentWidget());
QPainterPath backgroundShape;
if (!applet || applet->backgroundHints() == StandardBackground) {
//FIXME: a resize here is nasty, but perhaps still better than an eventfilter just for that..
if (parentWidget()->contentsRect().size() != size()) {
resize(parentWidget()->contentsRect().size());
}
backgroundShape = PaintUtils::roundedRectangle(contentsRect(), 5);
} else {
backgroundShape = shape();
}
painter->setRenderHints(QPainter::Antialiasing);
painter->fillPath(backgroundShape, wash);
}
} // namespace Plasma
#include "graphicsviewappletprivate_p.h"