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