kdelibs/kdeui/util/kpassivepopup.cpp
2014-11-13 01:04:59 +02:00

642 lines
17 KiB
C++

/*
* 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 "kpassivepopup.h"
#include "kpassivepopup.moc"
// Qt
#include <QApplication>
#include <QBitmap>
#include <QLabel>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPolygonF>
#include <QTimer>
#include <QToolTip>
#include <QSystemTrayIcon>
#include <kvbox.h>
#include <kdebug.h>
#include <kdialog.h>
#include <kglobalsettings.h>
#include <kconfig.h>
#ifdef Q_WS_X11
#include <qx11info_x11.h>
#include <netwm.h>
#endif
#include <config.h>
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 )
{
}
int popupStyle;
QPolygon surround;
QPoint anchor;
QPoint fixedPosition;
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( 0, f ? f : POPUP_FLAGS ),
d(new Private())
{
init( parent ? parent->effectiveWinId() : 0L );
}
KPassivePopup::KPassivePopup( WId win )
: QFrame( 0 ),
d(new Private())
{
init( win );
}
#if 0 // These break macos and win32 where the definition of WId makes them ambiguous
KPassivePopup::KPassivePopup( int popupStyle, QWidget *parent, Qt::WindowFlags f )
: QFrame( 0, f ? f : POPUP_FLAGS ),
d(new Private())
{
init( parent ? parent->winId() : 0L );
setPopupStyle( popupStyle );
}
KPassivePopup::KPassivePopup( int popupStyle, WId win, Qt::WindowFlags f )
: QFrame( 0, f ? f : POPUP_FLAGS ),
d(new Private())
{
init( win );
setPopupStyle( popupStyle );
}
#endif
void KPassivePopup::init( WId window )
{
d->window = window;
d->hideTimer = new QTimer( this );
setWindowFlags( POPUP_FLAGS );
setFrameStyle( QFrame::Box| QFrame::Plain );
setLineWidth( 2 );
if( d->popupStyle == Boxed )
{
setFrameStyle( QFrame::Box| QFrame::Plain );
setLineWidth( 2 );
}
else if( d->popupStyle == Balloon )
{
setPalette(QToolTip::palette());
//XXX dead ? setAutoMask(true);
}
connect( d->hideTimer, SIGNAL(timeout()), SLOT(hide()) );
connect( this, SIGNAL(clicked()), SLOT(hide()) );
}
KPassivePopup::~KPassivePopup()
{
delete d;
}
void KPassivePopup::setPopupStyle( int popupstyle )
{
if ( d->popupStyle == popupstyle )
return;
d->popupStyle = popupstyle;
if( d->popupStyle == Boxed )
{
setFrameStyle( QFrame::Box| QFrame::Plain );
setLineWidth( 2 );
}
else if( d->popupStyle == Balloon )
{
setPalette(QToolTip::palette());
//XXX dead ? setAutoMask(true);
}
}
void KPassivePopup::setView( QWidget *child )
{
delete d->msgView;
d->msgView = child;
delete d->topLayout;
d->topLayout = new QVBoxLayout( this );
if ( d->popupStyle == Balloon ) {
d->topLayout->setMargin( 2 * KDialog::marginHint() );
}
d->topLayout->addWidget( d->msgView );
d->topLayout->activate();
}
void KPassivePopup::setView( const QString &caption, const QString &text,
const QPixmap &icon )
{
// kDebug() << "KPassivePopup::setView " << caption << ", " << text;
setView( standardView( caption, text, icon, this ) );
}
KVBox * KPassivePopup::standardView( const QString& caption,
const QString& text,
const QPixmap& icon,
QWidget *parent )
{
KVBox *vb = new KVBox( parent ? parent : this );
vb->setSpacing( -1 );
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 );
}
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 );
if ( hb )
hb->setStretchFactor( d->ttl, 10 ); // enforce centering
}
if ( !text.isEmpty() ) {
d->msg = new QLabel( text, vb );
d->msg->setAlignment( Qt::AlignLeft );
d->msg->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
d->msg->setOpenExternalLinks(true);
}
return vb;
}
void KPassivePopup::setView( const QString &caption, const QString &text )
{
setView( caption, text, QPixmap() );
}
QWidget *KPassivePopup::view() const
{
return d->msgView;
}
int KPassivePopup::timeout() const
{
return d->hideDelay;
}
void KPassivePopup::setTimeout( int delay )
{
d->hideDelay = delay;
if( d->hideTimer->isActive() )
{
if( delay ) {
d->hideTimer->start( delay );
} else {
d->hideTimer->stop();
}
}
}
bool KPassivePopup::autoDelete() const
{
return d->autoDelete;
}
void KPassivePopup::setAutoDelete( bool autoDelete )
{
d->autoDelete = autoDelete;
}
void KPassivePopup::mouseReleaseEvent( QMouseEvent *e )
{
emit clicked();
emit clicked( e->pos() );
}
//
// Main Implementation
//
void KPassivePopup::setVisible( bool visible )
{
if (! visible ) {
QFrame::setVisible( visible );
return;
}
if ( size() != sizeHint() )
resize( sizeHint() );
if ( d->fixedPosition.isNull() )
positionSelf();
else {
if( d->popupStyle == Balloon )
setAnchor( d->fixedPosition );
else
move( d->fixedPosition );
}
QFrame::setVisible( /*visible=*/ true );
int delay = d->hideDelay;
if ( delay < 0 ) {
delay = DEFAULT_POPUP_TIME;
}
if ( delay > 0 ) {
d->hideTimer->start( delay );
}
}
void KPassivePopup::show()
{
QFrame::show();
}
void KPassivePopup::show(const QPoint &p)
{
d->fixedPosition = p;
show();
}
void KPassivePopup::hideEvent( QHideEvent * )
{
d->hideTimer->stop();
if ( d->autoDelete )
deleteLater();
}
QRect KPassivePopup::defaultArea() const
{
#ifdef Q_WS_X11
NETRootInfo info( QX11Info::display(),
NET::NumberOfDesktops |
NET::CurrentDesktop |
NET::WorkArea,
-1, false );
info.activate();
NETRect workArea = info.workArea( info.currentDesktop() );
QRect r;
r.setRect( workArea.pos.x, workArea.pos.y, 0, 0 ); // top left
#else
// FIX IT
QRect r;
r.setRect( 100, 100, 200, 200 ); // top left
#endif
return r;
}
void KPassivePopup::positionSelf()
{
QRect target;
#ifdef Q_WS_X11
if ( !d->window ) {
target = defaultArea();
}
else {
NETWinInfo ni( QX11Info::display(), d->window, QX11Info::appRootWindow(),
NET::WMIconGeometry );
// Figure out where to put the popup. Note that we must handle
// windows that skip the taskbar cleanly
if ( ni.state() & NET::SkipTaskbar ) {
target = defaultArea();
}
else {
NETRect r = ni.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;
ni.kdeGeometry( dummy, r );
target.setRect( r.pos.x, r.pos.y,
r.size.width, r.size.height);
}
}
}
#else
target = defaultArea();
#endif
moveNear( target );
}
void KPassivePopup::moveNear( const QRect &target )
{
QPoint pos = calculateNearbyPoint(target);
if( d->popupStyle == Balloon )
setAnchor( pos );
else
move( pos.x(), pos.y() );
}
QPoint KPassivePopup::calculateNearbyPoint( const QRect &target) {
QPoint pos = target.topLeft();
int x = pos.x();
int y = pos.y();
int w = minimumSizeHint().width();
int h = minimumSizeHint().height();
QRect r = KGlobalSettings::desktopGeometry(QPoint(x+w/2,y+h/2));
if( d->popupStyle == Balloon )
{
// find a point to anchor to
if( x + w > r.width() ){
x = x + target.width();
}
if( y + h > r.height() ){
y = y + target.height();
}
} else
{
if ( x < r.center().x() )
x = x + target.width();
else
x = x - w;
// It's apparently trying to go off screen, so display it ALL at the bottom.
if ( (y + h) > r.bottom() )
y = r.bottom() - h;
if ( (x + w) > r.right() )
x = r.right() - w;
}
if ( y < r.top() )
y = r.top();
if ( x < r.left() )
x = r.left();
return QPoint( x, y );
}
QPoint KPassivePopup::anchor() const
{
return d->anchor;
}
void KPassivePopup::setAnchor(const QPoint &anchor)
{
d->anchor = anchor;
updateMask();
}
void KPassivePopup::paintEvent( QPaintEvent* pe )
{
if( d->popupStyle == Balloon )
{
QPainter p;
p.begin( this );
p.drawPolygon( d->surround );
} else
QFrame::paintEvent( pe );
}
void KPassivePopup::updateMask()
{
// get screen-geometry for screen our anchor is on
// (geometry can differ from screen to screen!
QRect deskRect = KGlobalSettings::desktopGeometry(d->anchor);
int xh = 70, xl = 40;
if( width() < 80 )
xh = xl = 40;
else if( width() < 110 )
xh = width() - 40;
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 )
};
QBitmap mask( width(), height() );
mask.clear();
QPainter p( &mask );
QBrush brush( Qt::color1, Qt::SolidPattern );
p.setBrush( brush );
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);
QPolygon corner = path.toFillPolygon().toPolygon();
d->surround.resize( z + corner.count() - 1 );
for (int s = 1; s < corner.count() - 1; s++, z++) {
d->surround.setPoint( z, corner[s] );
}
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 ) );
} 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 ) );
}
} 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 ) );
} 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 ) );
}
}
}
d->surround.resize( z + 1 );
d->surround.setPoint( z, d->surround[0] );
p.drawPolygon( d->surround );
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() ) );
update();
}
//
// Convenience Methods
//
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout )
{
return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
}
KPassivePopup *KPassivePopup::message( const QString &text, QWidget *parent )
{
return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
QWidget *parent )
{
return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
const QPixmap &icon, WId parent, int timeout )
{
return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
const QPixmap &icon,
QSystemTrayIcon *parent, int timeout )
{
return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
}
KPassivePopup *KPassivePopup::message( const QString &text, QSystemTrayIcon *parent )
{
return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
QSystemTrayIcon *parent )
{
return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout )
{
KPassivePopup *pop = new KPassivePopup( parent );
pop->setPopupStyle( popupStyle );
pop->setAutoDelete( true );
pop->setView( caption, text, icon );
pop->d->hideDelay = timeout;
pop->show();
return pop;
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QWidget *parent )
{
return message( popupStyle, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
QWidget *parent )
{
return message( popupStyle, caption, text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon, WId parent, int timeout )
{
KPassivePopup *pop = new KPassivePopup( parent );
pop->setPopupStyle( popupStyle );
pop->setAutoDelete( true );
pop->setView( caption, text, icon );
pop->d->hideDelay = timeout;
pop->show();
return pop;
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
QSystemTrayIcon *parent, int timeout )
{
KPassivePopup *pop = new KPassivePopup( );
pop->setPopupStyle( popupStyle );
pop->setAutoDelete( true );
pop->setView( caption, text, icon );
pop->d->hideDelay = timeout;
QPoint pos = pop->calculateNearbyPoint(parent->geometry());
pop->show(pos);
pop->moveNear(parent->geometry());
return pop;
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QSystemTrayIcon *parent )
{
return message( popupStyle, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
QSystemTrayIcon *parent )
{
return message( popupStyle, caption, text, QPixmap(), parent );
}
// Local Variables:
// c-basic-offset: 4
// End: