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 if ( d->popupStyle == Balloon ) {
00175 d->topLayout->setMargin( 2 * KDialog::marginHint() );
00176 }
00177 d->topLayout->addWidget( d->msgView );
00178 d->topLayout->activate();
00179 }
00180
00181 void KPassivePopup::setView( const QString &caption, const QString &text,
00182 const QPixmap &icon )
00183 {
00184
00185 setView( standardView( caption, text, icon, this ) );
00186 }
00187
00188
00189 KVBox * KPassivePopup::standardView( const QString& caption,
00190 const QString& text,
00191 const QPixmap& icon,
00192 QWidget *parent )
00193 {
00194 KVBox *vb = new KVBox( parent ? parent : this );
00195 vb->setSpacing( -1 );
00196
00197 KHBox *hb=0;
00198 if ( !icon.isNull() ) {
00199 hb = new KHBox( vb );
00200 hb->setMargin( 0 );
00201 hb->setSpacing( -1 );
00202 d->ttlIcon = new QLabel( hb );
00203 d->ttlIcon->setPixmap( icon );
00204 d->ttlIcon->setAlignment( Qt::AlignLeft );
00205 }
00206
00207 if ( !caption.isEmpty() ) {
00208 d->ttl = new QLabel( caption, hb ? hb : vb );
00209 QFont fnt = d->ttl->font();
00210 fnt.setBold( true );
00211 d->ttl->setFont( fnt );
00212 d->ttl->setAlignment( Qt::AlignHCenter );
00213
00214 if ( hb )
00215 hb->setStretchFactor( d->ttl, 10 );
00216 }
00217
00218 if ( !text.isEmpty() ) {
00219 d->msg = new QLabel( text, vb );
00220 d->msg->setAlignment( Qt::AlignLeft );
00221 d->msg->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
00222 d->msg->setOpenExternalLinks(true);
00223 }
00224
00225 return vb;
00226 }
00227
00228 void KPassivePopup::setView( const QString &caption, const QString &text )
00229 {
00230 setView( caption, text, QPixmap() );
00231 }
00232
00233 QWidget *KPassivePopup::view() const
00234 {
00235 return d->msgView;
00236 }
00237
00238 int KPassivePopup::timeout() const
00239 {
00240 return d->hideDelay;
00241 }
00242
00243 void KPassivePopup::setTimeout( int delay )
00244 {
00245 d->hideDelay = delay;
00246 if( d->hideTimer->isActive() )
00247 {
00248 if( delay ) {
00249 d->hideTimer->start( delay );
00250 } else {
00251 d->hideTimer->stop();
00252 }
00253 }
00254 }
00255
00256 bool KPassivePopup::autoDelete() const
00257 {
00258 return d->autoDelete;
00259 }
00260
00261 void KPassivePopup::setAutoDelete( bool autoDelete )
00262 {
00263 d->autoDelete = autoDelete;
00264 }
00265
00266 void KPassivePopup::mouseReleaseEvent( QMouseEvent *e )
00267 {
00268 emit clicked();
00269 emit clicked( e->pos() );
00270 }
00271
00272
00273
00274
00275
00276 void KPassivePopup::setVisible( bool visible )
00277 {
00278 if (! visible ) {
00279 QFrame::setVisible( visible );
00280 return;
00281 }
00282
00283 if ( size() != sizeHint() )
00284 resize( sizeHint() );
00285
00286 if ( d->fixedPosition.isNull() )
00287 positionSelf();
00288 else {
00289 if( d->popupStyle == Balloon )
00290 setAnchor( d->fixedPosition );
00291 else
00292 move( d->fixedPosition );
00293 }
00294 QFrame::setVisible( true );
00295
00296 int delay = d->hideDelay;
00297 if ( delay < 0 ) {
00298 delay = DEFAULT_POPUP_TIME;
00299 }
00300
00301 if ( delay > 0 ) {
00302 d->hideTimer->start( delay );
00303 }
00304 }
00305
00306 void KPassivePopup::show()
00307 {
00308 QFrame::show();
00309 }
00310
00311 void KPassivePopup::show(const QPoint &p)
00312 {
00313 d->fixedPosition = p;
00314 show();
00315 }
00316
00317 void KPassivePopup::hideEvent( QHideEvent * )
00318 {
00319 d->hideTimer->stop();
00320 if ( d->autoDelete )
00321 deleteLater();
00322 }
00323
00324 QRect KPassivePopup::defaultArea() const
00325 {
00326 #ifdef Q_WS_X11
00327 NETRootInfo info( QX11Info::display(),
00328 NET::NumberOfDesktops |
00329 NET::CurrentDesktop |
00330 NET::WorkArea,
00331 -1, false );
00332 info.activate();
00333 NETRect workArea = info.workArea( info.currentDesktop() );
00334 QRect r;
00335 r.setRect( workArea.pos.x, workArea.pos.y, 0, 0 );
00336 #else
00337
00338 QRect r;
00339 r.setRect( 100, 100, 200, 200 );
00340 #endif
00341 return r;
00342 }
00343
00344 void KPassivePopup::positionSelf()
00345 {
00346 QRect target;
00347
00348 #ifdef Q_WS_X11
00349 if ( !d->window ) {
00350 target = defaultArea();
00351 }
00352
00353 else {
00354 NETWinInfo ni( QX11Info::display(), d->window, QX11Info::appRootWindow(),
00355 NET::WMIconGeometry );
00356
00357
00358
00359 if ( ni.state() & NET::SkipTaskbar ) {
00360 target = defaultArea();
00361 }
00362 else {
00363 NETRect r = ni.iconGeometry();
00364 target.setRect( r.pos.x, r.pos.y, r.size.width, r.size.height );
00365 if ( target.isNull() ) {
00366 NETRect dummy;
00367 ni.kdeGeometry( dummy, r );
00368 target.setRect( r.pos.x, r.pos.y,
00369 r.size.width, r.size.height);
00370 }
00371 }
00372 }
00373 #else
00374 target = defaultArea();
00375 #endif
00376 moveNear( target );
00377 }
00378
00379 void KPassivePopup::moveNear( const QRect &target )
00380 {
00381 QPoint pos = calculateNearbyPoint(target);
00382 if( d->popupStyle == Balloon )
00383 setAnchor( pos );
00384 else
00385 move( pos.x(), pos.y() );
00386 }
00387
00388 QPoint KPassivePopup::calculateNearbyPoint( const QRect &target) {
00389 QPoint pos = target.topLeft();
00390 int x = pos.x();
00391 int y = pos.y();
00392 int w = minimumSizeHint().width();
00393 int h = minimumSizeHint().height();
00394
00395 QRect r = KGlobalSettings::desktopGeometry(QPoint(x+w/2,y+h/2));
00396
00397 if( d->popupStyle == Balloon )
00398 {
00399
00400 if( x + w > r.width() ){
00401 x = x + target.width();
00402 }
00403
00404 if( y + h > r.height() ){
00405 y = y + target.height();
00406 }
00407 } else
00408 {
00409 if ( x < r.center().x() )
00410 x = x + target.width();
00411 else
00412 x = x - w;
00413
00414
00415 if ( (y + h) > r.bottom() )
00416 y = r.bottom() - h;
00417
00418 if ( (x + w) > r.right() )
00419 x = r.right() - w;
00420 }
00421 if ( y < r.top() )
00422 y = r.top();
00423
00424 if ( x < r.left() )
00425 x = r.left();
00426
00427 return QPoint( x, y );
00428 }
00429
00430 QPoint KPassivePopup::anchor() const
00431 {
00432 return d->anchor;
00433 }
00434
00435 void KPassivePopup::setAnchor(const QPoint &anchor)
00436 {
00437 d->anchor = anchor;
00438 updateMask();
00439 }
00440
00441 void KPassivePopup::paintEvent( QPaintEvent* pe )
00442 {
00443 if( d->popupStyle == Balloon )
00444 {
00445 QPainter p;
00446 p.begin( this );
00447 p.drawPolygon( d->surround );
00448 } else
00449 QFrame::paintEvent( pe );
00450 }
00451
00452 void KPassivePopup::updateMask()
00453 {
00454
00455
00456 QRect deskRect = KGlobalSettings::desktopGeometry(d->anchor);
00457
00458 int xh = 70, xl = 40;
00459 if( width() < 80 )
00460 xh = xl = 40;
00461 else if( width() < 110 )
00462 xh = width() - 40;
00463
00464 bool bottom = (d->anchor.y() + height()) > ((deskRect.y() + deskRect.height()-48));
00465 bool right = (d->anchor.x() + width()) > ((deskRect.x() + deskRect.width()-48));
00466
00467 QPoint corners[4] = {
00468 QPoint( width() - 50, 10 ),
00469 QPoint( 10, 10 ),
00470 QPoint( 10, height() - 50 ),
00471 QPoint( width() - 50, height() - 50 )
00472 };
00473
00474 QBitmap mask( width(), height() );
00475 mask.clear();
00476 QPainter p( &mask );
00477 QBrush brush( Qt::color1, Qt::SolidPattern );
00478 p.setBrush( brush );
00479
00480 int i = 0, z = 0;
00481 for (; i < 4; ++i) {
00482 QPainterPath path;
00483 path.moveTo(corners[i].x(),corners[i].y());
00484 path.arcTo(corners[i].x(),corners[i].y(),40,40, i * 90 , 90);
00485 QPolygon corner = path.toFillPolygon().toPolygon();
00486
00487 d->surround.resize( z + corner.count() - 1 );
00488 for (int s = 1; s < corner.count() - 1; s++, z++) {
00489 d->surround.setPoint( z, corner[s] );
00490 }
00491
00492 if (bottom && i == 2) {
00493 if (right) {
00494 d->surround.resize( z + 3 );
00495 d->surround.setPoint( z++, QPoint( width() - xh, height() - 10 ) );
00496 d->surround.setPoint( z++, QPoint( width() - 20, height() ) );
00497 d->surround.setPoint( z++, QPoint( width() - xl, height() - 10 ) );
00498 } else {
00499 d->surround.resize( z + 3 );
00500 d->surround.setPoint( z++, QPoint( xl, height() - 10 ) );
00501 d->surround.setPoint( z++, QPoint( 20, height() ) );
00502 d->surround.setPoint( z++, QPoint( xh, height() - 10 ) );
00503 }
00504 } else if (!bottom && i == 0) {
00505 if (right) {
00506 d->surround.resize( z + 3 );
00507 d->surround.setPoint( z++, QPoint( width() - xl, 10 ) );
00508 d->surround.setPoint( z++, QPoint( width() - 20, 0 ) );
00509 d->surround.setPoint( z++, QPoint( width() - xh, 10 ) );
00510 } else {
00511 d->surround.resize( z + 3 );
00512 d->surround.setPoint( z++, QPoint( xh, 10 ) );
00513 d->surround.setPoint( z++, QPoint( 20, 0 ) );
00514 d->surround.setPoint( z++, QPoint( xl, 10 ) );
00515 }
00516 }
00517 }
00518
00519 d->surround.resize( z + 1 );
00520 d->surround.setPoint( z, d->surround[0] );
00521 p.drawPolygon( d->surround );
00522 setMask(mask);
00523
00524 move( right ? d->anchor.x() - width() + 20 : ( d->anchor.x() < 11 ? 11 : d->anchor.x() - 20 ),
00525 bottom ? d->anchor.y() - height() : ( d->anchor.y() < 11 ? 11 : d->anchor.y() ) );
00526
00527 update();
00528 }
00529
00530
00531
00532
00533
00534 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00535 const QPixmap &icon,
00536 QWidget *parent, int timeout )
00537 {
00538 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
00539 }
00540
00541 KPassivePopup *KPassivePopup::message( const QString &text, QWidget *parent )
00542 {
00543 return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
00544 }
00545
00546 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00547 QWidget *parent )
00548 {
00549 return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
00550 }
00551
00552 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00553 const QPixmap &icon, WId parent, int timeout )
00554 {
00555 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
00556 }
00557
00558 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00559 const QPixmap &icon,
00560 QSystemTrayIcon *parent, int timeout )
00561 {
00562 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
00563 }
00564
00565 KPassivePopup *KPassivePopup::message( const QString &text, QSystemTrayIcon *parent )
00566 {
00567 return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
00568 }
00569
00570 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
00571 QSystemTrayIcon *parent )
00572 {
00573 return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
00574 }
00575
00576
00577 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00578 const QPixmap &icon,
00579 QWidget *parent, int timeout )
00580 {
00581 KPassivePopup *pop = new KPassivePopup( parent );
00582 pop->setPopupStyle( popupStyle );
00583 pop->setAutoDelete( true );
00584 pop->setView( caption, text, icon );
00585 pop->d->hideDelay = timeout;
00586 pop->show();
00587
00588 return pop;
00589 }
00590
00591 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QWidget *parent )
00592 {
00593 return message( popupStyle, QString(), text, QPixmap(), parent );
00594 }
00595
00596 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00597 QWidget *parent )
00598 {
00599 return message( popupStyle, caption, text, QPixmap(), parent );
00600 }
00601
00602 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00603 const QPixmap &icon, WId parent, int timeout )
00604 {
00605 KPassivePopup *pop = new KPassivePopup( parent );
00606 pop->setPopupStyle( popupStyle );
00607 pop->setAutoDelete( true );
00608 pop->setView( caption, text, icon );
00609 pop->d->hideDelay = timeout;
00610 pop->show();
00611
00612 return pop;
00613 }
00614
00615 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00616 const QPixmap &icon,
00617 QSystemTrayIcon *parent, int timeout )
00618 {
00619 KPassivePopup *pop = new KPassivePopup( );
00620 pop->setPopupStyle( popupStyle );
00621 pop->setAutoDelete( true );
00622 pop->setView( caption, text, icon );
00623 pop->d->hideDelay = timeout;
00624 QPoint pos = pop->calculateNearbyPoint(parent->geometry());
00625 pop->show(pos);
00626 pop->moveNear(parent->geometry());
00627
00628 return pop;
00629 }
00630
00631 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QSystemTrayIcon *parent )
00632 {
00633 return message( popupStyle, QString(), text, QPixmap(), parent );
00634 }
00635
00636 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
00637 QSystemTrayIcon *parent )
00638 {
00639 return message( popupStyle, caption, text, QPixmap(), parent );
00640 }
00641
00642
00643
00644
00645