kpassivepopup.cpp
00001 /* 00002 * copyright : (C) 2001-2002 by Richard Moore 00003 * copyright : (C) 2004-2005 by Sascha Cunz 00004 * License : This file is released under the terms of the LGPL, version 2. 00005 * email : rich@kde.org 00006 * email : sascha.cunz@tiscali.de 00007 */ 00008 00009 #include <kconfig.h> 00010 00011 #include <qapplication.h> 00012 #include <qlabel.h> 00013 #include <qlayout.h> 00014 #include <qtimer.h> 00015 #include <qvbox.h> 00016 #include <qpainter.h> 00017 #include <qtooltip.h> 00018 #include <qbitmap.h> 00019 #include <qpointarray.h> 00020 00021 #include <kdebug.h> 00022 #include <kdialog.h> 00023 #include <kpixmap.h> 00024 #include <kpixmapeffect.h> 00025 #include <kglobalsettings.h> 00026 00027 #include "config.h" 00028 #ifdef Q_WS_X11 00029 #include <netwm.h> 00030 #endif 00031 00032 #include "kpassivepopup.h" 00033 #include "kpassivepopup.moc" 00034 00035 class KPassivePopup::Private 00036 { 00037 public: 00038 int popupStyle; 00039 QPointArray surround; 00040 QPoint anchor; 00041 QPoint fixedPosition; 00042 }; 00043 00044 static const int DEFAULT_POPUP_TYPE = KPassivePopup::Boxed; 00045 static const int DEFAULT_POPUP_TIME = 6*1000; 00046 static const int POPUP_FLAGS = Qt::WStyle_Customize | Qt::WDestructiveClose | Qt::WX11BypassWM 00047 | Qt::WStyle_StaysOnTop | Qt::WStyle_Tool | Qt::WStyle_NoBorder; 00048 00049 KPassivePopup::KPassivePopup( QWidget *parent, const char *name, WFlags f ) 00050 : QFrame( 0, name, f ? f : POPUP_FLAGS ), 00051 window( parent ? parent->winId() : 0L ), msgView( 0 ), topLayout( 0 ), 00052 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new QTimer( this, "hide_timer" ) ), 00053 m_autoDelete( false ) 00054 { 00055 init( DEFAULT_POPUP_TYPE ); 00056 } 00057 00058 KPassivePopup::KPassivePopup( WId win, const char *name, WFlags f ) 00059 : QFrame( 0, name, f ? f : POPUP_FLAGS ), 00060 window( win ), msgView( 0 ), topLayout( 0 ), 00061 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new QTimer( this, "hide_timer" ) ), 00062 m_autoDelete( false ) 00063 { 00064 init( DEFAULT_POPUP_TYPE ); 00065 } 00066 00067 KPassivePopup::KPassivePopup( int popupStyle, QWidget *parent, const char *name, WFlags f ) 00068 : QFrame( 0, name, f ? f : POPUP_FLAGS ), 00069 window( parent ? parent->winId() : 0L ), msgView( 0 ), topLayout( 0 ), 00070 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new QTimer( this, "hide_timer" ) ), 00071 m_autoDelete( false ) 00072 { 00073 init( popupStyle ); 00074 } 00075 00076 KPassivePopup::KPassivePopup( int popupStyle, WId win, const char *name, WFlags f ) 00077 : QFrame( 0, name, f ? f : POPUP_FLAGS ), 00078 window( win ), msgView( 0 ), topLayout( 0 ), 00079 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new QTimer( this, "hide_timer" ) ), 00080 m_autoDelete( false ) 00081 { 00082 init( popupStyle ); 00083 } 00084 00085 void KPassivePopup::init( int popupStyle ) 00086 { 00087 d = new Private; 00088 d->popupStyle = popupStyle; 00089 if( popupStyle == Boxed ) 00090 { 00091 setFrameStyle( QFrame::Box| QFrame::Plain ); 00092 setLineWidth( 2 ); 00093 } 00094 else if( popupStyle == Balloon ) 00095 { 00096 setPalette(QToolTip::palette()); 00097 setAutoMask(TRUE); 00098 } 00099 connect( hideTimer, SIGNAL( timeout() ), SLOT( hide() ) ); 00100 connect( this, SIGNAL( clicked() ), SLOT( hide() ) ); 00101 } 00102 00103 KPassivePopup::~KPassivePopup() 00104 { 00105 delete d; 00106 } 00107 00108 void KPassivePopup::setView( QWidget *child ) 00109 { 00110 delete msgView; 00111 msgView = child; 00112 00113 delete topLayout; 00114 topLayout = new QVBoxLayout( this, d->popupStyle == Balloon ? 22 : KDialog::marginHint(), KDialog::spacingHint() ); 00115 topLayout->addWidget( msgView ); 00116 topLayout->activate(); 00117 } 00118 00119 void KPassivePopup::setView( const QString &caption, const QString &text, 00120 const QPixmap &icon ) 00121 { 00122 // kdDebug() << "KPassivePopup::setView " << caption << ", " << text << endl; 00123 setView( standardView( caption, text, icon, this ) ); 00124 } 00125 00126 QVBox * KPassivePopup::standardView( const QString& caption, 00127 const QString& text, 00128 const QPixmap& icon, 00129 QWidget *parent ) 00130 { 00131 QVBox *vb = new QVBox( parent ? parent : this ); 00132 vb->setSpacing( KDialog::spacingHint() ); 00133 00134 QHBox *hb=0; 00135 if ( !icon.isNull() ) { 00136 hb = new QHBox( vb ); 00137 hb->setMargin( 0 ); 00138 hb->setSpacing( KDialog::spacingHint() ); 00139 ttlIcon = new QLabel( hb, "title_icon" ); 00140 ttlIcon->setPixmap( icon ); 00141 ttlIcon->setAlignment( AlignLeft ); 00142 } 00143 00144 if ( !caption.isEmpty() ) { 00145 ttl = new QLabel( caption, hb ? hb : vb, "title_label" ); 00146 QFont fnt = ttl->font(); 00147 fnt.setBold( true ); 00148 ttl->setFont( fnt ); 00149 ttl->setAlignment( Qt::AlignHCenter ); 00150 if ( hb ) 00151 hb->setStretchFactor( ttl, 10 ); // enforce centering 00152 } 00153 00154 if ( !text.isEmpty() ) { 00155 msg = new QLabel( text, vb, "msg_label" ); 00156 msg->setAlignment( AlignLeft ); 00157 } 00158 00159 return vb; 00160 } 00161 00162 void KPassivePopup::setView( const QString &caption, const QString &text ) 00163 { 00164 setView( caption, text, QPixmap() ); 00165 } 00166 00167 void KPassivePopup::setTimeout( int delay ) 00168 { 00169 hideDelay = delay; 00170 if( hideTimer->isActive() ) 00171 { 00172 if( delay ) { 00173 hideTimer->changeInterval( delay ); 00174 } else { 00175 hideTimer->stop(); 00176 } 00177 } 00178 } 00179 00180 void KPassivePopup::setAutoDelete( bool autoDelete ) 00181 { 00182 m_autoDelete = autoDelete; 00183 } 00184 00185 void KPassivePopup::mouseReleaseEvent( QMouseEvent *e ) 00186 { 00187 emit clicked(); 00188 emit clicked( e->pos() ); 00189 } 00190 00191 // 00192 // Main Implementation 00193 // 00194 00195 void KPassivePopup::show() 00196 { 00197 if ( size() != sizeHint() ) 00198 resize( sizeHint() ); 00199 00200 if ( d->fixedPosition.isNull() ) 00201 positionSelf(); 00202 else { 00203 if( d->popupStyle == Balloon ) 00204 setAnchor( d->fixedPosition ); 00205 else 00206 move( d->fixedPosition ); 00207 } 00208 QFrame::show(); 00209 00210 int delay = hideDelay; 00211 if ( delay < 0 ) { 00212 delay = DEFAULT_POPUP_TIME; 00213 } 00214 00215 if ( delay > 0 ) { 00216 hideTimer->start( delay ); 00217 } 00218 } 00219 00220 void KPassivePopup::show(const QPoint &p) 00221 { 00222 d->fixedPosition = p; 00223 show(); 00224 } 00225 00226 void KPassivePopup::hideEvent( QHideEvent * ) 00227 { 00228 hideTimer->stop(); 00229 if ( m_autoDelete ) 00230 deleteLater(); 00231 } 00232 00233 QRect KPassivePopup::defaultArea() const 00234 { 00235 #ifdef Q_WS_X11 00236 NETRootInfo info( qt_xdisplay(), 00237 NET::NumberOfDesktops | 00238 NET::CurrentDesktop | 00239 NET::WorkArea, 00240 -1, false ); 00241 info.activate(); 00242 NETRect workArea = info.workArea( info.currentDesktop() ); 00243 QRect r; 00244 r.setRect( workArea.pos.x, workArea.pos.y, 0, 0 ); // top left 00245 #else 00246 // FIX IT 00247 QRect r; 00248 r.setRect( 100, 100, 200, 200 ); // top left 00249 #endif 00250 return r; 00251 } 00252 00253 void KPassivePopup::positionSelf() 00254 { 00255 QRect target; 00256 00257 #ifdef Q_WS_X11 00258 if ( !window ) { 00259 target = defaultArea(); 00260 } 00261 00262 else { 00263 NETWinInfo ni( qt_xdisplay(), window, qt_xrootwin(), 00264 NET::WMIconGeometry | NET::WMKDESystemTrayWinFor ); 00265 00266 // Figure out where to put the popup. Note that we must handle 00267 // windows that skip the taskbar cleanly 00268 if ( ni.kdeSystemTrayWinFor() ) { 00269 NETRect frame, win; 00270 ni.kdeGeometry( frame, win ); 00271 target.setRect( win.pos.x, win.pos.y, 00272 win.size.width, win.size.height ); 00273 } 00274 else if ( ni.state() & NET::SkipTaskbar ) { 00275 target = defaultArea(); 00276 } 00277 else { 00278 NETRect r = ni.iconGeometry(); 00279 target.setRect( r.pos.x, r.pos.y, r.size.width, r.size.height ); 00280 if ( target.isNull() ) { // bogus value, use the exact position 00281 NETRect dummy; 00282 ni.kdeGeometry( dummy, r ); 00283 target.setRect( r.pos.x, r.pos.y, 00284 r.size.width, r.size.height); 00285 } 00286 } 00287 } 00288 #else 00289 target = defaultArea(); 00290 #endif 00291 moveNear( target ); 00292 } 00293 00294 void KPassivePopup::moveNear( QRect target ) 00295 { 00296 QPoint pos = target.topLeft(); 00297 int x = pos.x(); 00298 int y = pos.y(); 00299 int w = width(); 00300 int h = height(); 00301 00302 QRect r = KGlobalSettings::desktopGeometry(QPoint(x+w/2,y+h/2)); 00303 00304 if( d->popupStyle == Balloon ) 00305 { 00306 // find a point to anchor to 00307 if( x + w > r.width() ){ 00308 x = x + target.width(); 00309 } 00310 00311 if( y + h > r.height() ){ 00312 y = y + target.height(); 00313 } 00314 } else 00315 { 00316 if ( x < r.center().x() ) 00317 x = x + target.width(); 00318 else 00319 x = x - w; 00320 00321 // It's apparently trying to go off screen, so display it ALL at the bottom. 00322 if ( (y + h) > r.bottom() ) 00323 y = r.bottom() - h; 00324 00325 if ( (x + w) > r.right() ) 00326 x = r.right() - w; 00327 } 00328 if ( y < r.top() ) 00329 y = r.top(); 00330 00331 if ( x < r.left() ) 00332 x = r.left(); 00333 00334 if( d->popupStyle == Balloon ) 00335 setAnchor( QPoint( x, y ) ); 00336 else 00337 move( x, y ); 00338 } 00339 00340 void KPassivePopup::setAnchor(const QPoint &anchor) 00341 { 00342 d->anchor = anchor; 00343 updateMask(); 00344 } 00345 00346 void KPassivePopup::paintEvent( QPaintEvent* pe ) 00347 { 00348 if( d->popupStyle == Balloon ) 00349 { 00350 QPainter p; 00351 p.begin( this ); 00352 p.drawPolygon( d->surround ); 00353 } else 00354 QFrame::paintEvent( pe ); 00355 } 00356 00357 void KPassivePopup::updateMask() 00358 { 00359 // get screen-geometry for screen our anchor is on 00360 // (geometry can differ from screen to screen! 00361 QRect deskRect = KGlobalSettings::desktopGeometry(d->anchor); 00362 00363 int xh = 70, xl = 40; 00364 if( width() < 80 ) 00365 xh = xl = 40; 00366 else if( width() < 110 ) 00367 xh = width() - 40; 00368 00369 bool bottom = (d->anchor.y() + height()) > ((deskRect.y() + deskRect.height()-48)); 00370 bool right = (d->anchor.x() + width()) > ((deskRect.x() + deskRect.width()-48)); 00371 00372 QPoint corners[4] = { 00373 QPoint( width() - 50, 10 ), 00374 QPoint( 10, 10 ), 00375 QPoint( 10, height() - 50 ), 00376 QPoint( width() - 50, height() - 50 ) 00377 }; 00378 00379 QBitmap mask( width(), height(), true ); 00380 QPainter p( &mask ); 00381 QBrush brush( Qt::white, Qt::SolidPattern ); 00382 p.setBrush( brush ); 00383 00384 int i = 0, z = 0; 00385 for (; i < 4; ++i) { 00386 QPointArray corner; 00387 corner.makeArc(corners[i].x(), corners[i].y(), 40, 40, i * 16 * 90, 16 * 90); 00388 00389 d->surround.resize( z + corner.count() ); 00390 for (unsigned int s = 0; s < corner.count() - 1; s++) { 00391 d->surround.setPoint( z++, corner[s] ); 00392 } 00393 00394 if (bottom && i == 2) { 00395 if (right) { 00396 d->surround.resize( z + 3 ); 00397 d->surround.setPoint( z++, QPoint( width() - xh, height() - 11 ) ); 00398 d->surround.setPoint( z++, QPoint( width() - 20, height() ) ); 00399 d->surround.setPoint( z++, QPoint( width() - xl, height() - 11 ) ); 00400 } else { 00401 d->surround.resize( z + 3 ); 00402 d->surround.setPoint( z++, QPoint( xl, height() - 11 ) ); 00403 d->surround.setPoint( z++, QPoint( 20, height() ) ); 00404 d->surround.setPoint( z++, QPoint( xh, height() - 11 ) ); 00405 } 00406 } else if (!bottom && i == 0) { 00407 if (right) { 00408 d->surround.resize( z + 3 ); 00409 d->surround.setPoint( z++, QPoint( width() - xl, 10 ) ); 00410 d->surround.setPoint( z++, QPoint( width() - 20, 0 ) ); 00411 d->surround.setPoint( z++, QPoint( width() - xh, 10 ) ); 00412 } else { 00413 d->surround.resize( z + 3 ); 00414 d->surround.setPoint( z++, QPoint( xh, 10 ) ); 00415 d->surround.setPoint( z++, QPoint( 20, 0 ) ); 00416 d->surround.setPoint( z++, QPoint( xl, 10 ) ); 00417 } 00418 } 00419 } 00420 00421 d->surround.resize( z + 1 ); 00422 d->surround.setPoint( z, d->surround[0] ); 00423 p.drawPolygon( d->surround ); 00424 setMask(mask); 00425 00426 move( right ? d->anchor.x() - width() + 20 : ( d->anchor.x() < 11 ? 11 : d->anchor.x() - 20 ), 00427 bottom ? d->anchor.y() - height() : ( d->anchor.y() < 11 ? 11 : d->anchor.y() ) ); 00428 00429 update(); 00430 } 00431 00432 // 00433 // Convenience Methods 00434 // 00435 00436 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text, 00437 const QPixmap &icon, 00438 QWidget *parent, const char *name, int timeout ) 00439 { 00440 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, name, timeout ); 00441 } 00442 00443 KPassivePopup *KPassivePopup::message( const QString &text, QWidget *parent, const char *name ) 00444 { 00445 return message( DEFAULT_POPUP_TYPE, QString::null, text, QPixmap(), parent, name ); 00446 } 00447 00448 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text, 00449 QWidget *parent, const char *name ) 00450 { 00451 return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent, name ); 00452 } 00453 00454 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text, 00455 const QPixmap &icon, WId parent, const char *name, int timeout ) 00456 { 00457 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, name, timeout ); 00458 } 00459 00460 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text, 00461 const QPixmap &icon, 00462 QWidget *parent, const char *name, int timeout ) 00463 { 00464 KPassivePopup *pop = new KPassivePopup( popupStyle, parent, name ); 00465 pop->setAutoDelete( true ); 00466 pop->setView( caption, text, icon ); 00467 pop->hideDelay = timeout; 00468 pop->show(); 00469 00470 return pop; 00471 } 00472 00473 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QWidget *parent, const char *name ) 00474 { 00475 return message( popupStyle, QString::null, text, QPixmap(), parent, name ); 00476 } 00477 00478 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text, 00479 QWidget *parent, const char *name ) 00480 { 00481 return message( popupStyle, caption, text, QPixmap(), parent, name ); 00482 } 00483 00484 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text, 00485 const QPixmap &icon, WId parent, const char *name, int timeout ) 00486 { 00487 KPassivePopup *pop = new KPassivePopup( popupStyle, parent, name ); 00488 pop->setAutoDelete( true ); 00489 pop->setView( caption, text, icon ); 00490 pop->hideDelay = timeout; 00491 pop->show(); 00492 00493 return pop; 00494 } 00495 00496 // Local Variables: 00497 // c-basic-offset: 4 00498 // End: