00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "kpassivepopup.h"
00024 #include "kpassivepopup.moc"
00025
00026
00027 #include <QApplication>
00028 #include <QBitmap>
00029 #include <QLabel>
00030 #include <QLayout>
00031 #include <QMouseEvent>
00032 #include <QPainter>
00033 #include <QPainterPath>
00034 #include <QPolygonF>
00035 #include <QTimer>
00036 #include <QToolTip>
00037 #include <QSystemTrayIcon>
00038
00039 #include <kvbox.h>
00040 #include <kdebug.h>
00041 #include <kdialog.h>
00042 #include <kglobalsettings.h>
00043
00044 #include <kconfig.h>
00045
00046 #ifdef Q_WS_X11
00047 #include <qx11info_x11.h>
00048 #include <netwm.h>
00049 #endif
00050
00051 #include <config.h>
00052
00053 static const int DEFAULT_POPUP_TYPE = KPassivePopup::Boxed;
00054 static const int DEFAULT_POPUP_TIME = 6*1000;
00055 static const Qt::WindowFlags POPUP_FLAGS = Qt::Tool | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint;
00056
00057 class KPassivePopup::Private
00058 {
00059 public:
00060 Private()
00061 : popupStyle( DEFAULT_POPUP_TYPE ),
00062 msgView(0),
00063 topLayout(0),
00064 hideDelay( DEFAULT_POPUP_TIME ),
00065 hideTimer(0),
00066 autoDelete( false )
00067 {
00068
00069 }
00070
00071 int popupStyle;
00072 QPolygon surround;
00073 QPoint anchor;
00074 QPoint fixedPosition;
00075
00076 WId window;
00077 QWidget *msgView;
00078 QBoxLayout *topLayout;
00079 int hideDelay;
00080 QTimer *hideTimer;
00081
00082 QLabel *ttlIcon;
00083 QLabel *ttl;
00084 QLabel *msg;
00085
00086 bool autoDelete;
00087 };
00088
00089 KPassivePopup::KPassivePopup( QWidget *parent, Qt::WFlags f )
00090 : QFrame( 0, f ? f : POPUP_FLAGS ),
00091 d(new Private())
00092 {
00093 init( parent ? parent->effectiveWinId() : 0L );
00094 }
00095
00096 KPassivePopup::KPassivePopup( WId win )
00097 : QFrame( 0 ),
00098 d(new Private())
00099 {
00100 init( win );
00101 }
00102
00103 #if 0 // These break macos and win32 where the definition of WId makes them ambiguous
00104 KPassivePopup::KPassivePopup( int popupStyle, QWidget *parent, Qt::WFlags f )
00105 : QFrame( 0, f ? f : POPUP_FLAGS ),
00106 d(new Private())
00107 {
00108 init( parent ? parent->winId() : 0L );
00109 setPopupStyle( popupStyle );
00110 }
00111
00112 KPassivePopup::KPassivePopup( int popupStyle, WId win, Qt::WFlags f )
00113 : QFrame( 0, f ? f : POPUP_FLAGS ),
00114 d(new Private())
00115 {
00116 init( win );
00117 setPopupStyle( popupStyle );
00118 }
00119 #endif
00120
00121 void KPassivePopup::init( WId window )
00122 {
00123 d->window = window;
00124 d->hideTimer = new QTimer( this );
00125
00126 setWindowFlags( POPUP_FLAGS );
00127 setFrameStyle( QFrame::Box| QFrame::Plain );
00128 setLineWidth( 2 );
00129
00130 if( d->popupStyle == Boxed )
00131 {
00132 setFrameStyle( QFrame::Box| QFrame::Plain );
00133 setLineWidth( 2 );
00134 }
00135 else if( d->popupStyle == Balloon )
00136 {
00137 setPalette(QToolTip::palette());
00138
00139 }
00140 connect( d->hideTimer, SIGNAL( timeout() ), SLOT( hide() ) );
00141 connect( this, SIGNAL( clicked() ), SLOT( hide() ) );
00142 }
00143
00144 KPassivePopup::~KPassivePopup()
00145 {
00146 delete d;
00147 }
00148
00149 void KPassivePopup::setPopupStyle( int popupstyle )
00150 {
00151 if ( d->popupStyle == popupstyle )
00152 return;
00153
00154 d->popupStyle = popupstyle;
00155 if( d->popupStyle == Boxed )
00156 {
00157 setFrameStyle( QFrame::Box| QFrame::Plain );
00158 setLineWidth( 2 );
00159 }
00160 else if( d->popupStyle == Balloon )
00161 {
00162 setPalette(QToolTip::palette());
00163
00164 }
00165 }
00166
00167 void KPassivePopup::setView( QWidget *child )
00168 {
00169 delete d->msgView;
00170 d->msgView = child;
00171
00172 delete d->topLayout;
00173 d->topLayout = new QVBoxLayout( this );
00174 d->topLayout->setMargin( d->popupStyle == Balloon ? 22 : KDialog::marginHint() );
00175 d->topLayout->setSpacing( KDialog::spacingHint() );
00176 d->topLayout->addWidget( d->msgView );
00177 d->topLayout->activate();
00178 }
00179
00180 void KPassivePopup::setView( const QString &caption, const QString &text,
00181 const QPixmap &icon )
00182 {
00183
00184 setView( standardView( caption, text, icon, this ) );
00185 }
00186
00187
00188 KVBox * KPassivePopup::standardView( const QString& caption,
00189 const QString& text,
00190 const QPixmap& icon,
00191 QWidget *parent )
00192 {
00193 KVBox *vb = new KVBox( parent ? parent : this );
00194 vb->setSpacing( KDialog::spacingHint() );
00195
00196 KHBox *hb=0;
00197 if ( !icon.isNull() ) {
00198 hb = new KHBox( vb );
00199 hb->setMargin( 0 );
00200 hb->setSpacing( KDialog::spacingHint() );
00201 d->ttlIcon = new QLabel( hb );
00202 d->ttlIcon->setPixmap( icon );
00203 d->ttlIcon->setAlignment( Qt::AlignLeft );
00204 }
00205
00206 if ( !caption.isEmpty() ) {
00207 d->ttl = new QLabel( caption, hb ? hb : vb );
00208 QFont fnt = d->ttl->font();
00209 fnt.setBold( true );
00210 d->ttl->setFont( fnt );
00211 d->ttl->setAlignment( Qt::AlignHCenter );
00212
00213 if ( hb )
00214 hb->setStretchFactor( d->ttl, 10 );
00215 }
00216
00217 if ( !text.isEmpty() ) {
00218 d->msg = new QLabel( text, vb );
00219 d->msg->setAlignment( Qt::AlignLeft );
00220 d->msg->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
00221 d->msg->setOpenExternalLinks(true);
00222 }
00223
00224 return vb;
00225 }
00226
00227 void KPassivePopup::setView( const QString &caption, const QString &text )
00228 {
00229 setView( caption, text, QPixmap() );
00230 }
00231
00232 QWidget *KPassivePopup::view() const
00233 {
00234 return d->msgView;
00235 }
00236
00237 int KPassivePopup::timeout() const
00238 {
00239 return d->hideDelay;
00240 }
00241
00242 void KPassivePopup::setTimeout( int delay )
00243 {
00244 d->hideDelay = delay;
00245 if( d->hideTimer->isActive() )
00246 {
00247 if( delay ) {
00248 d->hideTimer->start( delay );
00249 } else {
00250 d->hideTimer->stop();
00251 }
00252 }
00253 }
00254
00255 bool KPassivePopup::autoDelete() const
00256 {
00257 return d->autoDelete;
00258 }
00259
00260 void KPassivePopup::setAutoDelete( bool autoDelete )
00261 {
00262 d->autoDelete = autoDelete;
00263 }
00264
00265 void KPassivePopup::mouseReleaseEvent( QMouseEvent *e )
00266 {
00267 emit clicked();
00268 emit clicked( e->pos() );
00269 }
00270
00271
00272
00273
00274
00275 void KPassivePopup::setVisible( bool visible )
00276 {
00277 if (! visible ) {
00278 QFrame::setVisible( visible );
00279 return;
00280 }
00281
00282 if ( size() != sizeHint() )
00283 resize( sizeHint() );
00284
00285 if ( d->fixedPosition.isNull() )
00286 positionSelf();
00287 else {
00288 if( d->popupStyle == Balloon )
00289 setAnchor( d->fixedPosition );
00290 else
00291 move( d->fixedPosition );
00292 }
00293 QFrame::setVisible( true );
00294
00295 int delay = d->hideDelay;
00296 if ( delay < 0 ) {
00297 delay = DEFAULT_POPUP_TIME;
00298 }
00299
00300 if ( delay > 0 ) {
00301 d->hideTimer->start( delay );
00302 }
00303 }
00304
00305 void KPassivePopup::show()
00306 {
00307 QFrame::show();
00308 }
00309
00310 void KPassivePopup::show(const QPoint &p)
00311 {
00312 d->fixedPosition = p;
00313 show();
00314 }
00315
00316 void KPassivePopup::hideEvent( QHideEvent * )
00317 {
00318 d->hideTimer->stop();
00319 if ( d->autoDelete )
00320 deleteLater();
00321 }
00322
00323 QRect KPassivePopup::defaultArea() const
00324 {
00325 #ifdef Q_WS_X11
00326 NETRootInfo info( QX11Info::display(),
00327 NET::NumberOfDesktops |
00328 NET::CurrentDesktop |
00329 NET::WorkArea,
00330 -1, false );
00331 info.activate();
00332 NETRect workArea = info.workArea( info.currentDesktop() );
00333 QRect r;
00334 r.setRect( workArea.pos.x, workArea.pos.y, 0, 0 );
00335 #else
00336
00337 QRect r;
00338 r.setRect( 100, 100, 200, 200 );
00339 #endif
00340 return r;
00341 }
00342
00343 void KPassivePopup::positionSelf()
00344 {
00345 QRect target;
00346
00347 #ifdef Q_WS_X11
00348 if ( !d->window ) {
00349 target = defaultArea();
00350 }
00351
00352 else {
00353 NETWinInfo ni( QX11Info::display(), d->window, QX11Info::appRootWindow(),
00354 NET::WMIconGeometry );
00355
00356
00357
00358 if ( ni.state() & NET::SkipTaskbar ) {
00359 target = defaultArea();
00360 }
00361 else {
00362 NETRect r = ni.iconGeometry();
00363 target.setRect( r.pos.x, r.pos.y, r.size.width, r.size.height );
00364 if ( target.isNull() ) {
00365 NETRect dummy;
00366 ni.kdeGeometry( dummy, r );
00367 target.setRect( r.pos.x, r.pos.y,
00368 r.size.width, r.size.height);
00369 }
00370 }
00371 }
00372 #else
00373 target = defaultArea();
00374 #endif
00375 moveNear( target );
00376 }
00377
00378 void KPassivePopup::moveNear( const QRect &target )
00379 {
00380 QPoint pos = calculateNearbyPoint(target);
00381 if( d->popupStyle == Balloon )
00382 setAnchor( pos );
00383 else
00384 move( pos.x(), pos.y() );
00385 }
00386
00387 QPoint KPassivePopup::calculateNearbyPoint( const QRect &target) {
00388 QPoint pos = target.topLeft();
00389 int x = pos.x();
00390 int y = pos.y();
00391 int w = minimumSizeHint().width();
00392 int h = minimumSizeHint().height();
00393
00394 QRect r = KGlobalSettings::desktopGeometry(QPoint(x+w/2,y+h/2));
00395
00396 if( d->popupStyle == Balloon )
00397 {
00398
00399 if( x + w > r.width() ){
00400 x = x + target.width();
00401 }
00402
00403 if( y + h > r.height() ){
00404 y = y + target.height();
00405 }
00406 } else
00407 {
00408 if ( x < r.center().x() )
00409 x = x + target.width();
00410 else
00411 x = x - w;
00412
00413
00414 if ( (y + h) > r.bottom() )
00415 y = r.bottom() - h;
00416
00417 if ( (x + w) > r.right() )
00418 x = r.right() - w;
00419 }
00420 if ( y < r.top() )
00421 y = r.top();
00422
00423 if ( x < r.left() )
00424 x = r.left();
00425
00426 return QPoint( x, y );
00427 }
00428
00429 QPoint KPassivePopup::anchor() const
00430 {
00431 return d->anchor;
00432 }
00433
00434 void KPassivePopup::setAnchor(const QPoint &anchor)
00435 {
00436 d->anchor = anchor;
00437 updateMask();
00438 }
00439
00440 void KPassivePopup::paintEvent( QPaintEvent* pe )
00441 {
00442 if( d->popupStyle == Balloon )
00443 {
00444 QPainter p;
00445 p.begin( this );
00446 p.drawPolygon( d->surround );
00447 } else
00448 QFrame::paintEvent( pe );
00449 }
00450
00451 void KPassivePopup::updateMask()
00452 {
00453
00454
00455 QRect deskRect = KGlobalSettings::desktopGeometry(d->anchor);
00456
00457 int xh = 70, xl = 40;
00458 if( width() < 80 )
00459 xh = xl = 40;
00460 else if( width() < 110 )
00461 xh = width() - 40;
00462
00463 bool bottom = (d->anchor.y() + height()) > ((deskRect.y() + deskRect.height()-48));
00464 bool right = (d->anchor.x() + width()) > ((deskRect.x() + deskRect.width()-48));
00465
00466 QPoint corners[4] = {
00467 QPoint( width() - 50, 10 ),
00468 QPoint( 10, 10 ),
00469 QPoint( 10, height() - 50 ),
00470 QPoint( width() - 50, height() - 50 )
00471 };
00472
00473 QBitmap mask( width(), height() );
00474 mask.clear();
00475 QPainter p( &mask );
00476 QBrush brush( Qt::color1, Qt::SolidPattern );
00477 p.setBrush( brush );
00478
00479 int i = 0, z = 0;
00480 for (; i < 4; ++i) {
00481 QPainterPath path;
00482 path.moveTo(corners[i].x(),corners[i].y());
00483 path.arcTo(corners[i].x(),corners[i].y(),40,40, i * 90 , 90);
00484 QPolygon corner = path.toFillPolygon().toPolygon();
00485
00486 d->surround.resize( z + corner.count() - 1 );
00487 for (int s = 1; s < corner.count() - 1; s++, z++) {
00488 d->surround.setPoint( z, corner[s] );
00489 }
00490
00491 if (bottom && i == 2) {
00492 if (right) {
00493 d->surround.resize( z + 3 );
00494 d->surround.setPoint( z++, QPoint( width() - xh, height() - 10 ) );
00495 d->surround.setPoint( z++, QPoint( width() - 20, height() ) );
00496 d->surround.setPoint( z++, QPoint( width() - xl, height() - 10 ) );
00497 } else {
00498 d->surround.resize( z + 3 );
00499 d->surround.setPoint( z++, QPoint( xl, height() - 10 ) );
00500 d->surround.setPoint( z++, QPoint( 20, height() ) );
00501 d->surround.setPoint( z++, QPoint( xh, height() - 10 ) );
00502 }
00503 } else if (!bottom && i == 0) {
00504 if (right) {
00505 d->surround.resize( z + 3 );
00506 d->surround.setPoint( z++, QPoint( width() - xl, 10 ) );
00507 d->surround.setPoint( z++, QPoint( width() - 20, 0 ) );
00508 d->surround.setPoint( z++, QPoint( width() - xh, 10 ) );
00509 } else {
00510 d->surround.resize( z + 3 );
00511 d->surround.setPoint( z++, QPoint( xh, 10 ) );
00512 d->surround.setPoint( z++, QPoint( 20, 0 ) );
00513 d->surround.setPoint( z++, QPoint( xl, 10 ) );
00514 }
00515 }
00516 }
00517
00518 d->surround.resize( z + 1 );
00519 d->surround.setPoint( z, d->surround[0] );
00520 p.drawPolygon( d->surround );
00521 setMask(mask);
00522
00523 move( right ? d->anchor.x() - width() + 20 : ( d->anchor.x() < 11 ? 11 : d->anchor.x() - 20 ),
00524 bottom ? d->anchor.y() - height() : ( d->anchor.y() < 11 ? 11 : d->anchor.y() ) );
00525
00526 update();
00527 }
00528
00529
00530
00531
00532
00533 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00534 const QPixmap &icon,
00535 QWidget *parent, int timeout )
00536 {
00537 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
00538 }
00539
00540 KPassivePopup *KPassivePopup::message( const QString &text, QWidget *parent )
00541 {
00542 return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
00543 }
00544
00545 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00546 QWidget *parent )
00547 {
00548 return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
00549 }
00550
00551 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00552 const QPixmap &icon, WId parent, int timeout )
00553 {
00554 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
00555 }
00556
00557 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00558 const QPixmap &icon,
00559 QSystemTrayIcon *parent, int timeout )
00560 {
00561 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
00562 }
00563
00564 KPassivePopup *KPassivePopup::message( const QString &text, QSystemTrayIcon *parent )
00565 {
00566 return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
00567 }
00568
00569 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00570 QSystemTrayIcon *parent )
00571 {
00572 return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
00573 }
00574
00575
00576 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00577 const QPixmap &icon,
00578 QWidget *parent, int timeout )
00579 {
00580 KPassivePopup *pop = new KPassivePopup( parent );
00581 pop->setPopupStyle( popupStyle );
00582 pop->setAutoDelete( true );
00583 pop->setView( caption, text, icon );
00584 pop->d->hideDelay = timeout;
00585 pop->show();
00586
00587 return pop;
00588 }
00589
00590 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QWidget *parent )
00591 {
00592 return message( popupStyle, QString(), text, QPixmap(), parent );
00593 }
00594
00595 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00596 QWidget *parent )
00597 {
00598 return message( popupStyle, caption, text, QPixmap(), parent );
00599 }
00600
00601 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00602 const QPixmap &icon, WId parent, int timeout )
00603 {
00604 KPassivePopup *pop = new KPassivePopup( parent );
00605 pop->setPopupStyle( popupStyle );
00606 pop->setAutoDelete( true );
00607 pop->setView( caption, text, icon );
00608 pop->d->hideDelay = timeout;
00609 pop->show();
00610
00611 return pop;
00612 }
00613
00614 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00615 const QPixmap &icon,
00616 QSystemTrayIcon *parent, int timeout )
00617 {
00618 KPassivePopup *pop = new KPassivePopup( );
00619 pop->setPopupStyle( popupStyle );
00620 pop->setAutoDelete( true );
00621 pop->setView( caption, text, icon );
00622 pop->d->hideDelay = timeout;
00623 QPoint pos = pop->calculateNearbyPoint(parent->geometry());
00624 pop->show(pos);
00625 pop->moveNear(parent->geometry());
00626
00627 return pop;
00628 }
00629
00630 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QSystemTrayIcon *parent )
00631 {
00632 return message( popupStyle, QString(), text, QPixmap(), parent );
00633 }
00634
00635 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00636 QSystemTrayIcon *parent )
00637 {
00638 return message( popupStyle, caption, text, QPixmap(), parent );
00639 }
00640
00641
00642
00643
00644