/* * 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(KIcon("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(KIcon("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(KIcon("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(KIcon("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, KIcon("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"