/* * Copyright (C) 2001-2006 by Richard Moore * Copyright (C) 2004-2005 by Sascha Cunz * * 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 "moc_kpassivepopup.cpp" // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_WS_X11 #include #include #endif #include 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: