kdelibs/kdeui/util/kpassivepopup.cpp

556 lines
14 KiB
C++
Raw Permalink Normal View History

2014-11-13 01:04:59 +02:00
/*
* Copyright (C) 2001-2006 by Richard Moore <rich@kde.org>
* Copyright (C) 2004-2005 by Sascha Cunz <sascha.cunz@tiscali.de>
*
* This library 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 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*/
#include "config.h"
2014-11-13 01:04:59 +02:00
#include "kpassivepopup.h"
#include "kvbox.h"
#include "kdialog.h"
#include "kconfig.h"
#include "kdebug.h"
#include "netwm.h"
2014-11-13 01:04:59 +02:00
// Qt
#include <QApplication>
#include <QBitmap>
#include <QLabel>
#include <QLayout>
#include <QPaintEvent>
2014-11-13 01:04:59 +02:00
#include <QPainter>
#include <QPainterPath>
#include <QPolygon>
2014-11-13 01:04:59 +02:00
#include <QTimer>
#include <QToolTip>
#include <QDesktopWidget>
#include <QX11Info>
2014-11-13 01:04:59 +02:00
static const int DEFAULT_POPUP_TYPE = KPassivePopup::Boxed;
static const int DEFAULT_POPUP_TIME = 6*1000;
static const Qt::WindowFlags POPUP_FLAGS = Qt::Tool | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint;
class KPassivePopup::Private
{
public:
Private()
: popupStyle(DEFAULT_POPUP_TYPE),
msgView(0),
topLayout(0),
hideDelay(DEFAULT_POPUP_TIME),
hideTimer(0),
autoDelete(false)
2014-11-13 01:04:59 +02:00
{
}
int popupStyle;
QPolygon surround;
QPoint anchor;
QPoint fixedPosition;
2014-11-13 01:04:59 +02:00
WId window;
QWidget *msgView;
QBoxLayout *topLayout;
int hideDelay;
QTimer *hideTimer;
QLabel *ttlIcon;
QLabel *ttl;
QLabel *msg;
bool autoDelete;
};
KPassivePopup::KPassivePopup(QWidget *parent, Qt::WindowFlags f)
: QFrame(nullptr, f ? f : POPUP_FLAGS),
2014-11-13 01:04:59 +02:00
d(new Private())
{
init(parent ? parent->effectiveWinId() : 0);
2014-11-13 01:04:59 +02:00
}
KPassivePopup::KPassivePopup(WId win)
: QFrame(nullptr),
d(new Private())
2014-11-13 01:04:59 +02:00
{
init(win);
2014-11-13 01:04:59 +02:00
}
void KPassivePopup::init(WId window)
2014-11-13 01:04:59 +02:00
{
d->window = window;
d->hideTimer = new QTimer(this);
2014-11-13 01:04:59 +02:00
setWindowFlags(POPUP_FLAGS);
setFrameStyle(QFrame::Box| QFrame::Plain);
setLineWidth(2);
2014-11-13 01:04:59 +02:00
if (d->popupStyle == KPassivePopup::Boxed) {
setFrameStyle(QFrame::Box| QFrame::Plain);
setLineWidth(2);
} else if(d->popupStyle == KPassivePopup::Balloon) {
2014-11-13 01:04:59 +02:00
setPalette(QToolTip::palette());
// XXX dead ? setAutoMask(true);
2014-11-13 01:04:59 +02:00
}
connect(d->hideTimer, SIGNAL(timeout()), this, SLOT(hide()));
connect(this, SIGNAL(clicked()), this, SLOT(hide()));
2014-11-13 01:04:59 +02:00
}
KPassivePopup::~KPassivePopup()
{
delete d;
}
void KPassivePopup::setPopupStyle(int popupstyle)
2014-11-13 01:04:59 +02:00
{
if (d->popupStyle == popupstyle) {
return;
}
2014-11-13 01:04:59 +02:00
d->popupStyle = popupstyle;
if (d->popupStyle == KPassivePopup::Boxed) {
setFrameStyle(QFrame::Box| QFrame::Plain);
setLineWidth(2);
} else if (d->popupStyle == KPassivePopup::Balloon) {
2014-11-13 01:04:59 +02:00
setPalette(QToolTip::palette());
// XXX dead ? setAutoMask(true);
2014-11-13 01:04:59 +02:00
}
}
void KPassivePopup::setView(QWidget *child)
2014-11-13 01:04:59 +02:00
{
delete d->msgView;
d->msgView = child;
delete d->topLayout;
d->topLayout = new QVBoxLayout(this);
if (d->popupStyle == KPassivePopup::Balloon) {
d->topLayout->setMargin(2 * KDialog::marginHint());
2014-11-13 01:04:59 +02:00
}
d->topLayout->addWidget(d->msgView);
2014-11-13 01:04:59 +02:00
d->topLayout->activate();
}
void KPassivePopup::setView(const QString &caption, const QString &text,
const QPixmap &icon)
2014-11-13 01:04:59 +02:00
{
// kDebug() << "KPassivePopup::setView " << caption << ", " << text;
setView(standardView(caption, text, icon, this));
2014-11-13 01:04:59 +02:00
}
KVBox* KPassivePopup::standardView(const QString &caption,
const QString &text,
const QPixmap &icon,
QWidget *parent)
2014-11-13 01:04:59 +02:00
{
KVBox *vb = new KVBox(parent ? parent : this);
vb->setSpacing(-1);
2014-11-13 01:04:59 +02:00
KHBox *hb=0;
if (!icon.isNull()) {
hb = new KHBox(vb);
hb->setMargin(0);
hb->setSpacing(-1);
d->ttlIcon = new QLabel(hb);
d->ttlIcon->setPixmap(icon);
d->ttlIcon->setAlignment(Qt::AlignLeft);
2014-11-13 01:04:59 +02:00
}
if (!caption.isEmpty()) {
d->ttl = new QLabel(caption, hb ? hb : vb);
QFont fnt = d->ttl->font();
fnt.setBold(true);
d->ttl->setFont(fnt);
d->ttl->setAlignment(Qt::AlignHCenter);
2014-11-13 01:04:59 +02:00
if (hb) {
hb->setStretchFactor(d->ttl, 10); // enforce centering
}
2014-11-13 01:04:59 +02:00
}
if (!text.isEmpty()) {
d->msg = new QLabel(text, vb);
d->msg->setAlignment(Qt::AlignLeft);
2014-11-13 01:04:59 +02:00
d->msg->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
d->msg->setOpenExternalLinks(true);
}
return vb;
}
void KPassivePopup::setView(const QString &caption, const QString &text)
2014-11-13 01:04:59 +02:00
{
setView(caption, text, QPixmap());
2014-11-13 01:04:59 +02:00
}
QWidget* KPassivePopup::view() const
2014-11-13 01:04:59 +02:00
{
return d->msgView;
}
int KPassivePopup::timeout() const
{
return d->hideDelay;
}
void KPassivePopup::setTimeout(int delay)
2014-11-13 01:04:59 +02:00
{
d->hideDelay = delay;
if (d->hideTimer->isActive()) {
if (delay) {
d->hideTimer->start(delay);
2014-11-13 01:04:59 +02:00
} else {
d->hideTimer->stop();
}
}
}
bool KPassivePopup::autoDelete() const
{
return d->autoDelete;
}
void KPassivePopup::setAutoDelete(bool autoDelete)
2014-11-13 01:04:59 +02:00
{
d->autoDelete = autoDelete;
}
void KPassivePopup::mouseReleaseEvent(QMouseEvent *e)
2014-11-13 01:04:59 +02:00
{
emit clicked();
emit clicked(e->pos());
2014-11-13 01:04:59 +02:00
}
//
// Main Implementation
//
void KPassivePopup::setVisible(bool visible)
2014-11-13 01:04:59 +02:00
{
if (!visible) {
QFrame::setVisible(visible);
2014-11-13 01:04:59 +02:00
return;
}
if (size() != sizeHint()) {
resize(sizeHint());
}
2014-11-13 01:04:59 +02:00
if (d->fixedPosition.isNull()) {
2014-11-13 01:04:59 +02:00
positionSelf();
} else {
if (d->popupStyle == KPassivePopup::Balloon) {
setAnchor(d->fixedPosition);
} else {
move(d->fixedPosition);
}
2014-11-13 01:04:59 +02:00
}
QFrame::setVisible(/*visible=*/ true );
2014-11-13 01:04:59 +02:00
int delay = d->hideDelay;
if (delay < 0) {
2014-11-13 01:04:59 +02:00
delay = DEFAULT_POPUP_TIME;
}
if (delay > 0) {
d->hideTimer->start(delay);
2014-11-13 01:04:59 +02:00
}
}
void KPassivePopup::show()
{
QFrame::show();
}
void KPassivePopup::show(const QPoint &p)
{
d->fixedPosition = p;
show();
}
void KPassivePopup::hideEvent(QHideEvent *e)
2014-11-13 01:04:59 +02:00
{
Q_UNUSED(e);
2014-11-13 01:04:59 +02:00
d->hideTimer->stop();
if (d->autoDelete) {
2014-11-13 01:04:59 +02:00
deleteLater();
}
2014-11-13 01:04:59 +02:00
}
QRect KPassivePopup::defaultArea() const
{
NETRootInfo info(
QX11Info::display(),
NET::NumberOfDesktops | NET::CurrentDesktop | NET::WorkArea,
-1, false
);
2014-11-13 01:04:59 +02:00
info.activate();
NETRect workArea = info.workArea(info.currentDesktop());
return QRect(workArea.pos.x, workArea.pos.y, 0, 0); // top left
2014-11-13 01:04:59 +02:00
}
void KPassivePopup::positionSelf()
{
QRect target;
if (!d->window) {
2014-11-13 01:04:59 +02:00
target = defaultArea();
} else {
NETWinInfo info(
QX11Info::display(), d->window, QX11Info::appRootWindow(),
NET::WMIconGeometry
);
2014-11-13 01:04:59 +02:00
// Figure out where to put the popup. Note that we must handle
// windows that skip the taskbar cleanly
if (info.state() & NET::SkipTaskbar) {
2014-11-13 01:04:59 +02:00
target = defaultArea();
} else {
NETRect r = info.iconGeometry();
target.setRect(r.pos.x, r.pos.y, r.size.width, r.size.height);
if (target.isNull()) {
// bogus value, use the exact position
NETRect dummy;
info.kdeGeometry(dummy, r);
target.setRect(r.pos.x, r.pos.y, r.size.width, r.size.height);
}
2014-11-13 01:04:59 +02:00
}
}
moveNear(target);
2014-11-13 01:04:59 +02:00
}
void KPassivePopup::moveNear(const QRect &target)
2014-11-13 01:04:59 +02:00
{
QPoint pos = calculateNearbyPoint(target);
if (d->popupStyle == KPassivePopup::Balloon) {
setAnchor(pos);
} else {
move(pos.x(), pos.y());
}
2014-11-13 01:04:59 +02:00
}
QPoint KPassivePopup::calculateNearbyPoint(const QRect &target)
{
2014-11-13 01:04:59 +02:00
QPoint pos = target.topLeft();
int x = pos.x();
int y = pos.y();
int w = minimumSizeHint().width();
int h = minimumSizeHint().height();
QRect r = QApplication::desktop()->screenGeometry(QPoint(x + w / 2,y + h / 2));
2014-11-13 01:04:59 +02:00
if (d->popupStyle == KPassivePopup::Balloon) {
2014-11-13 01:04:59 +02:00
// find a point to anchor to
if (x + w > r.width()) {
2014-11-13 01:04:59 +02:00
x = x + target.width();
}
if (y + h > r.height()) {
2014-11-13 01:04:59 +02:00
y = y + target.height();
}
} else {
if (x < r.center().x()) {
2014-11-13 01:04:59 +02:00
x = x + target.width();
} else {
2014-11-13 01:04:59 +02:00
x = x - w;
}
2014-11-13 01:04:59 +02:00
// It's apparently trying to go off screen, so display it ALL at the bottom.
if ((y + h) > r.bottom()) {
2014-11-13 01:04:59 +02:00
y = r.bottom() - h;
}
2014-11-13 01:04:59 +02:00
if ((x + w) > r.right()) {
2014-11-13 01:04:59 +02:00
x = r.right() - w;
}
2014-11-13 01:04:59 +02:00
}
if (y < r.top()) {
2014-11-13 01:04:59 +02:00
y = r.top();
}
2014-11-13 01:04:59 +02:00
if (x < r.left()) {
2014-11-13 01:04:59 +02:00
x = r.left();
}
2014-11-13 01:04:59 +02:00
return QPoint(x, y);
2014-11-13 01:04:59 +02:00
}
QPoint KPassivePopup::anchor() const
{
return d->anchor;
}
void KPassivePopup::setAnchor(const QPoint &anchor)
{
d->anchor = anchor;
updateMask();
}
void KPassivePopup::paintEvent(QPaintEvent* pe)
2014-11-13 01:04:59 +02:00
{
if (d->popupStyle == KPassivePopup::Balloon) {
2014-11-13 01:04:59 +02:00
QPainter p;
p.begin(this);
p.drawPolygon(d->surround);
} else {
QFrame::paintEvent(pe);
}
2014-11-13 01:04:59 +02:00
}
void KPassivePopup::updateMask()
{
// get screen-geometry for screen our anchor is on
// (geometry can differ from screen to screen!
QRect deskRect = QApplication::desktop()->screenGeometry(d->anchor);
2014-11-13 01:04:59 +02:00
int xh = 70, xl = 40;
if (width() < 80) {
2014-11-13 01:04:59 +02:00
xh = xl = 40;
} else if(width() < 110) {
2014-11-13 01:04:59 +02:00
xh = width() - 40;
}
2014-11-13 01:04:59 +02:00
bool bottom = (d->anchor.y() + height()) > ((deskRect.y() + deskRect.height()-48));
bool right = (d->anchor.x() + width()) > ((deskRect.x() + deskRect.width()-48));
QPoint corners[4] = {
QPoint(width() - 50, 10),
QPoint(10, 10),
QPoint(10, height() - 50),
QPoint(width() - 50, height() - 50)
2014-11-13 01:04:59 +02:00
};
QBitmap mask(width(), height());
2014-11-13 01:04:59 +02:00
mask.clear();
QPainter p(&mask);
QBrush brush(Qt::color1, Qt::SolidPattern);
p.setBrush(brush);
2014-11-13 01:04:59 +02:00
int i = 0, z = 0;
for (; i < 4; ++i) {
QPainterPath path;
path.moveTo(corners[i].x(),corners[i].y());
path.arcTo(corners[i].x(),corners[i].y(), 40, 40, i * 90 , 90);
2014-11-13 01:04:59 +02:00
QPolygon corner = path.toFillPolygon().toPolygon();
d->surround.resize(z + corner.count() - 1);
2014-11-13 01:04:59 +02:00
for (int s = 1; s < corner.count() - 1; s++, z++) {
d->surround.setPoint(z, corner[s]);
2014-11-13 01:04:59 +02:00
}
if (bottom && i == 2) {
if (right) {
d->surround.resize(z + 3);
d->surround.setPoint(z++, QPoint(width() - xh, height() - 10));
d->surround.setPoint(z++, QPoint(width() - 20, height()));
d->surround.setPoint(z++, QPoint(width() - xl, height() - 10));
2014-11-13 01:04:59 +02:00
} else {
d->surround.resize(z + 3);
d->surround.setPoint(z++, QPoint(xl, height() - 10));
d->surround.setPoint(z++, QPoint(20, height()));
d->surround.setPoint(z++, QPoint(xh, height() - 10));
2014-11-13 01:04:59 +02:00
}
} else if (!bottom && i == 0) {
if (right) {
d->surround.resize(z + 3 );
d->surround.setPoint(z++, QPoint(width() - xl, 10));
d->surround.setPoint(z++, QPoint(width() - 20, 0));
d->surround.setPoint(z++, QPoint(width() - xh, 10));
2014-11-13 01:04:59 +02:00
} else {
d->surround.resize(z + 3 );
d->surround.setPoint(z++, QPoint( xh, 10));
d->surround.setPoint(z++, QPoint( 20, 0));
d->surround.setPoint(z++, QPoint( xl, 10));
2014-11-13 01:04:59 +02:00
}
}
}
d->surround.resize(z + 1);
d->surround.setPoint(z, d->surround[0]);
p.drawPolygon(d->surround);
2014-11-13 01:04:59 +02:00
setMask(mask);
move(
right ? d->anchor.x() - width() + 20 : (d->anchor.x() < 11 ? 11 : d->anchor.x() - 20),
bottom ? d->anchor.y() - height() : (d->anchor.y() < 11 ? 11 : d->anchor.y())
);
2014-11-13 01:04:59 +02:00
update();
}
//
// Convenience Methods
//
KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout)
2014-11-13 01:04:59 +02:00
{
return message(DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout);
2014-11-13 01:04:59 +02:00
}
KPassivePopup *KPassivePopup::message( const QString &text, QWidget *parent)
2014-11-13 01:04:59 +02:00
{
return message(DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent);
2014-11-13 01:04:59 +02:00
}
KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text,
QWidget *parent)
2014-11-13 01:04:59 +02:00
{
return message(DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent);
2014-11-13 01:04:59 +02:00
}
KPassivePopup *KPassivePopup::message(const QString &caption, const QString &text,
const QPixmap &icon, WId parent, int timeout)
2014-11-13 01:04:59 +02:00
{
return message(DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout);
2014-11-13 01:04:59 +02:00
}
KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout)
2014-11-13 01:04:59 +02:00
{
KPassivePopup *pop = new KPassivePopup(parent);
pop->setPopupStyle(popupStyle);
pop->setAutoDelete(true);
pop->setView(caption, text, icon);
2014-11-13 01:04:59 +02:00
pop->d->hideDelay = timeout;
pop->show();
return pop;
}
KPassivePopup *KPassivePopup::message(int popupStyle, const QString &text, QWidget *parent)
2014-11-13 01:04:59 +02:00
{
return message(popupStyle, QString(), text, QPixmap(), parent);
2014-11-13 01:04:59 +02:00
}
KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text,
QWidget *parent)
2014-11-13 01:04:59 +02:00
{
return message(popupStyle, caption, text, QPixmap(), parent);
2014-11-13 01:04:59 +02:00
}
KPassivePopup *KPassivePopup::message(int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon, WId parent, int timeout)
2014-11-13 01:04:59 +02:00
{
KPassivePopup *pop = new KPassivePopup(parent);
pop->setPopupStyle(popupStyle);
pop->setAutoDelete(true);
pop->setView(caption, text, icon);
2014-11-13 01:04:59 +02:00
pop->d->hideDelay = timeout;
pop->show();
return pop;
}
#include "moc_kpassivepopup.cpp"