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 <tdeconfig.h> 00010 00011 #include <tqapplication.h> 00012 #include <tqlabel.h> 00013 #include <tqlayout.h> 00014 #include <tqtimer.h> 00015 #include <tqvbox.h> 00016 #include <tqpainter.h> 00017 #include <tqtooltip.h> 00018 #include <tqbitmap.h> 00019 #include <tqpointarray.h> 00020 00021 #include <kdebug.h> 00022 #include <kdialog.h> 00023 #include <kpixmap.h> 00024 #include <kpixmapeffect.h> 00025 #include <tdeglobalsettings.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 TQPointArray surround; 00040 TQPoint anchor; 00041 TQPoint 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 = TQt::WStyle_Customize | TQt::WDestructiveClose | TQt::WX11BypassWM 00047 | TQt::WStyle_StaysOnTop | TQt::WStyle_Tool | TQt::WStyle_NoBorder; 00048 00049 KPassivePopup::KPassivePopup( TQWidget *parent, const char *name, WFlags f ) 00050 : TQFrame( 0, name, (WFlags)(f ? (int)f : POPUP_FLAGS) ), 00051 window( parent ? parent->winId() : 0L ), msgView( 0 ), topLayout( 0 ), 00052 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new TQTimer( 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 : TQFrame( 0, name, (WFlags)(f ? (int)f : POPUP_FLAGS) ), 00060 window( win ), msgView( 0 ), topLayout( 0 ), 00061 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new TQTimer( this, "hide_timer" ) ), 00062 m_autoDelete( false ) 00063 { 00064 init( DEFAULT_POPUP_TYPE ); 00065 } 00066 00067 KPassivePopup::KPassivePopup( int popupStyle, TQWidget *parent, const char *name, WFlags f ) 00068 : TQFrame( 0, name, (WFlags)(f ? (int)f : POPUP_FLAGS) ), 00069 window( parent ? parent->winId() : 0L ), msgView( 0 ), topLayout( 0 ), 00070 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new TQTimer( 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 : TQFrame( 0, name, (WFlags)(f ? (int)f : POPUP_FLAGS) ), 00078 window( win ), msgView( 0 ), topLayout( 0 ), 00079 hideDelay( DEFAULT_POPUP_TIME ), hideTimer( new TQTimer( 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( TQFrame::Box| TQFrame::Plain ); 00092 setLineWidth( 2 ); 00093 } 00094 else if( popupStyle == Balloon ) 00095 { 00096 setPalette(TQToolTip::palette()); 00097 setAutoMask(TRUE); 00098 } 00099 connect( hideTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( hide() ) ); 00100 connect( this, TQT_SIGNAL( clicked() ), TQT_SLOT( hide() ) ); 00101 } 00102 00103 KPassivePopup::~KPassivePopup() 00104 { 00105 delete d; 00106 } 00107 00108 void KPassivePopup::setView( TQWidget *child ) 00109 { 00110 delete msgView; 00111 msgView = child; 00112 00113 delete topLayout; 00114 topLayout = new TQVBoxLayout( this, d->popupStyle == Balloon ? 22 : KDialog::marginHint(), KDialog::spacingHint() ); 00115 topLayout->addWidget( msgView ); 00116 topLayout->activate(); 00117 } 00118 00119 void KPassivePopup::setView( const TQString &caption, const TQString &text, 00120 const TQPixmap &icon ) 00121 { 00122 // kdDebug() << "KPassivePopup::setView " << caption << ", " << text << endl; 00123 setView( standardView( caption, text, icon, this ) ); 00124 } 00125 00126 static void truncateStringToFit(TQString &string, TQFont font, int max_width) { 00127 bool truncated = false; 00128 TQFontMetrics fm(font); 00129 while (fm.width(string) > max_width) { 00130 string.truncate(string.length() - 1); 00131 truncated = true; 00132 } 00133 if (truncated) { 00134 string += " ..."; 00135 } 00136 } 00137 00138 TQVBox * KPassivePopup::standardView(const TQString& caption, 00139 const TQString& text, 00140 const TQPixmap& icon, 00141 TQWidget *parent) 00142 { 00143 TQString sizedCaption = caption; 00144 TQString sizedText = text; 00145 00146 #ifdef Q_WS_X11 00147 int max_width; 00148 00149 NETRootInfo info( tqt_xdisplay(), 00150 NET::NumberOfDesktops | 00151 NET::CurrentDesktop | 00152 NET::WorkArea, 00153 -1, false ); 00154 info.activate(); 00155 NETRect workArea = info.workArea(info.currentDesktop()); 00156 max_width = workArea.size.width / 3; 00157 #endif 00158 00159 TQVBox *vb = new TQVBox( parent ? parent : this ); 00160 vb->setSpacing( KDialog::spacingHint() ); 00161 00162 TQHBox *hb=0; 00163 if ( !icon.isNull() ) { 00164 hb = new TQHBox( vb ); 00165 hb->setMargin( 0 ); 00166 hb->setSpacing( KDialog::spacingHint() ); 00167 ttlIcon = new TQLabel( hb, "title_icon" ); 00168 ttlIcon->setPixmap( icon ); 00169 ttlIcon->setAlignment( AlignLeft ); 00170 } 00171 00172 if ( !sizedCaption.isEmpty() ) { 00173 ttl = new TQLabel( sizedCaption, hb ? hb : vb, "title_label" ); 00174 TQFont fnt = ttl->font(); 00175 #ifdef Q_WS_X11 00176 truncateStringToFit(sizedCaption, fnt, max_width); 00177 ttl->setText(sizedCaption); 00178 #endif 00179 fnt.setBold( true ); 00180 ttl->setFont( fnt ); 00181 ttl->setAlignment( Qt::AlignHCenter ); 00182 if ( hb ) { 00183 hb->setStretchFactor( ttl, 10 ); // enforce centering 00184 } 00185 } 00186 00187 if ( !sizedText.isEmpty() ) { 00188 msg = new TQLabel( sizedText, vb, "msg_label" ); 00189 #ifdef Q_WS_X11 00190 TQStringList textLines = TQStringList::split("\n", sizedText, true); 00191 for (TQStringList::Iterator it = textLines.begin(); it != textLines.end(); ++it) { 00192 truncateStringToFit(*it, msg->font(), max_width); 00193 } 00194 00195 // Limit message to 5 lines of text 00196 if (textLines.count() > 5) { 00197 int count = 3; 00198 TQStringList truncatedLines; 00199 for (TQStringList::Iterator it = textLines.begin(); it != textLines.end(); ++it) { 00200 truncatedLines.append(*it); 00201 if (count > 5) { 00202 truncatedLines.append("..."); 00203 break; 00204 } 00205 count++; 00206 } 00207 textLines = truncatedLines; 00208 } 00209 sizedText = textLines.join("\n"); 00210 msg->setText(sizedText); 00211 #endif 00212 msg->setAlignment( AlignLeft ); 00213 } 00214 00215 return vb; 00216 } 00217 00218 void KPassivePopup::setView( const TQString &caption, const TQString &text ) 00219 { 00220 setView( caption, text, TQPixmap() ); 00221 } 00222 00223 void KPassivePopup::setTimeout( int delay ) 00224 { 00225 hideDelay = delay; 00226 if( hideTimer->isActive() ) 00227 { 00228 if( delay ) { 00229 hideTimer->changeInterval( delay ); 00230 } else { 00231 hideTimer->stop(); 00232 } 00233 } 00234 } 00235 00236 void KPassivePopup::setAutoDelete( bool autoDelete ) 00237 { 00238 m_autoDelete = autoDelete; 00239 } 00240 00241 void KPassivePopup::mouseReleaseEvent( TQMouseEvent *e ) 00242 { 00243 emit clicked(); 00244 emit clicked( e->pos() ); 00245 } 00246 00247 // 00248 // Main Implementation 00249 // 00250 00251 void KPassivePopup::show() 00252 { 00253 TQSize desiredSize = sizeHint(); 00254 00255 if (size() != desiredSize) { 00256 resize(desiredSize); 00257 } 00258 00259 if (d->fixedPosition.isNull()) { 00260 positionSelf(); 00261 } 00262 else { 00263 if( d->popupStyle == Balloon ) { 00264 setAnchor(d->fixedPosition); 00265 } 00266 else { 00267 move(d->fixedPosition); 00268 } 00269 } 00270 TQFrame::show(); 00271 00272 int delay = hideDelay; 00273 if ( delay < 0 ) { 00274 delay = DEFAULT_POPUP_TIME; 00275 } 00276 00277 if ( delay > 0 ) { 00278 hideTimer->start( delay ); 00279 } 00280 } 00281 00282 void KPassivePopup::show(const TQPoint &p) 00283 { 00284 d->fixedPosition = p; 00285 show(); 00286 } 00287 00288 void KPassivePopup::hideEvent( TQHideEvent * ) 00289 { 00290 hideTimer->stop(); 00291 emit( hidden( this ) ); 00292 if ( m_autoDelete ) 00293 deleteLater(); 00294 } 00295 00296 TQRect KPassivePopup::defaultArea() const 00297 { 00298 #ifdef Q_WS_X11 00299 NETRootInfo info( tqt_xdisplay(), 00300 NET::NumberOfDesktops | 00301 NET::CurrentDesktop | 00302 NET::WorkArea, 00303 -1, false ); 00304 info.activate(); 00305 NETRect workArea = info.workArea( info.currentDesktop() ); 00306 TQRect r; 00307 r.setRect( workArea.pos.x, workArea.pos.y, 0, 0 ); // top left 00308 #else 00309 // FIX IT 00310 TQRect r; 00311 r.setRect( 100, 100, 200, 200 ); // top left 00312 #endif 00313 return r; 00314 } 00315 00316 void KPassivePopup::positionSelf() 00317 { 00318 TQRect target; 00319 00320 #ifdef Q_WS_X11 00321 if ( !window ) { 00322 target = defaultArea(); 00323 } 00324 00325 else { 00326 NETWinInfo ni( tqt_xdisplay(), window, tqt_xrootwin(), 00327 NET::WMIconGeometry | NET::WMKDESystemTrayWinFor ); 00328 00329 // Figure out where to put the popup. Note that we must handle 00330 // windows that skip the taskbar cleanly 00331 if ( ni.kdeSystemTrayWinFor() ) { 00332 NETRect frame, win; 00333 ni.kdeGeometry( frame, win ); 00334 target.setRect( win.pos.x, win.pos.y, 00335 win.size.width, win.size.height ); 00336 } 00337 else if ( ni.state() & NET::SkipTaskbar ) { 00338 target = defaultArea(); 00339 } 00340 else { 00341 NETRect r = ni.iconGeometry(); 00342 target.setRect( r.pos.x, r.pos.y, r.size.width, r.size.height ); 00343 if ( target.isNull() ) { // bogus value, use the exact position 00344 NETRect dummy; 00345 ni.kdeGeometry( dummy, r ); 00346 target.setRect( r.pos.x, r.pos.y, 00347 r.size.width, r.size.height); 00348 } 00349 } 00350 } 00351 #else 00352 target = defaultArea(); 00353 #endif 00354 moveNear( target ); 00355 } 00356 00357 void KPassivePopup::moveNear( TQRect target ) 00358 { 00359 TQPoint pos = target.topLeft(); 00360 int x = pos.x(); 00361 int y = pos.y(); 00362 int w = width(); 00363 int h = height(); 00364 00365 TQRect r = TDEGlobalSettings::desktopGeometry(TQPoint(x+w/2,y+h/2)); 00366 00367 if( d->popupStyle == Balloon ) 00368 { 00369 // find a point to anchor to 00370 if( x + w > r.width() ){ 00371 x = x + target.width(); 00372 } 00373 00374 if( y + h > r.height() ){ 00375 y = y + target.height(); 00376 } 00377 } else 00378 { 00379 if ( x < r.center().x() ) 00380 x = x + target.width(); 00381 else 00382 x = x - w; 00383 00384 // It's apparently trying to go off screen, so display it ALL at the bottom. 00385 if ( (y + h) > r.bottom() ) 00386 y = r.bottom() - h; 00387 00388 if ( (x + w) > r.right() ) 00389 x = r.right() - w; 00390 } 00391 if ( y < r.top() ) 00392 y = r.top(); 00393 00394 if ( x < r.left() ) 00395 x = r.left(); 00396 00397 if( d->popupStyle == Balloon ) 00398 setAnchor( TQPoint( x, y ) ); 00399 else 00400 move( x, y ); 00401 } 00402 00403 void KPassivePopup::setAnchor(const TQPoint &anchor) 00404 { 00405 d->anchor = anchor; 00406 updateMask(); 00407 } 00408 00409 void KPassivePopup::paintEvent( TQPaintEvent* pe ) 00410 { 00411 if( d->popupStyle == Balloon ) { 00412 TQPainter p; 00413 p.begin( this ); 00414 p.drawPolygon( d->surround ); 00415 } 00416 else { 00417 TQFrame::paintEvent( pe ); 00418 } 00419 } 00420 00421 void KPassivePopup::updateMask() 00422 { 00423 // get screen-geometry for screen our anchor is on 00424 // (geometry can differ from screen to screen! 00425 TQRect deskRect = TDEGlobalSettings::desktopGeometry(d->anchor); 00426 00427 int xh = 70, xl = 40; 00428 if( width() < 80 ) 00429 xh = xl = 40; 00430 else if( width() < 110 ) 00431 xh = width() - 40; 00432 00433 bool bottom = (d->anchor.y() + height()) > ((deskRect.y() + deskRect.height()-48)); 00434 bool right = (d->anchor.x() + width()) > ((deskRect.x() + deskRect.width()-48)); 00435 00436 TQPoint corners[4] = { 00437 TQPoint( width() - 50, 10 ), 00438 TQPoint( 10, 10 ), 00439 TQPoint( 10, height() - 50 ), 00440 TQPoint( width() - 50, height() - 50 ) 00441 }; 00442 00443 TQBitmap mask( width(), height(), true ); 00444 TQPainter p( &mask ); 00445 TQBrush brush( Qt::white, Qt::SolidPattern ); 00446 p.setBrush( brush ); 00447 00448 int i = 0, z = 0; 00449 for (; i < 4; ++i) { 00450 TQPointArray corner; 00451 corner.makeArc(corners[i].x(), corners[i].y(), 40, 40, i * 16 * 90, 16 * 90); 00452 00453 d->surround.resize( z + corner.count() ); 00454 for (unsigned int s = 0; s < corner.count() - 1; s++) { 00455 d->surround.setPoint( z++, corner[s] ); 00456 } 00457 00458 if (bottom && i == 2) { 00459 if (right) { 00460 d->surround.resize( z + 3 ); 00461 d->surround.setPoint( z++, TQPoint( width() - xh, height() - 11 ) ); 00462 d->surround.setPoint( z++, TQPoint( width() - 20, height() ) ); 00463 d->surround.setPoint( z++, TQPoint( width() - xl, height() - 11 ) ); 00464 } else { 00465 d->surround.resize( z + 3 ); 00466 d->surround.setPoint( z++, TQPoint( xl, height() - 11 ) ); 00467 d->surround.setPoint( z++, TQPoint( 20, height() ) ); 00468 d->surround.setPoint( z++, TQPoint( xh, height() - 11 ) ); 00469 } 00470 } else if (!bottom && i == 0) { 00471 if (right) { 00472 d->surround.resize( z + 3 ); 00473 d->surround.setPoint( z++, TQPoint( width() - xl, 10 ) ); 00474 d->surround.setPoint( z++, TQPoint( width() - 20, 0 ) ); 00475 d->surround.setPoint( z++, TQPoint( width() - xh, 10 ) ); 00476 } else { 00477 d->surround.resize( z + 3 ); 00478 d->surround.setPoint( z++, TQPoint( xh, 10 ) ); 00479 d->surround.setPoint( z++, TQPoint( 20, 0 ) ); 00480 d->surround.setPoint( z++, TQPoint( xl, 10 ) ); 00481 } 00482 } 00483 } 00484 00485 d->surround.resize( z + 1 ); 00486 d->surround.setPoint( z, d->surround[0] ); 00487 p.drawPolygon( d->surround ); 00488 setMask(mask); 00489 00490 move( right ? d->anchor.x() - width() + 20 : ( d->anchor.x() < 11 ? 11 : d->anchor.x() - 20 ), 00491 bottom ? d->anchor.y() - height() : ( d->anchor.y() < 11 ? 11 : d->anchor.y() ) ); 00492 00493 update(); 00494 } 00495 00496 // 00497 // Convenience Methods 00498 // 00499 00500 KPassivePopup *KPassivePopup::message( const TQString &caption, const TQString &text, 00501 const TQPixmap &icon, 00502 TQWidget *parent, const char *name, int timeout ) 00503 { 00504 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, name, timeout ); 00505 } 00506 00507 KPassivePopup *KPassivePopup::message( const TQString &text, TQWidget *parent, const char *name ) 00508 { 00509 return message( DEFAULT_POPUP_TYPE, TQString::null, text, TQPixmap(), parent, name ); 00510 } 00511 00512 KPassivePopup *KPassivePopup::message( const TQString &caption, const TQString &text, 00513 TQWidget *parent, const char *name ) 00514 { 00515 return message( DEFAULT_POPUP_TYPE, caption, text, TQPixmap(), parent, name ); 00516 } 00517 00518 KPassivePopup *KPassivePopup::message( const TQString &caption, const TQString &text, 00519 const TQPixmap &icon, WId parent, const char *name, int timeout ) 00520 { 00521 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, name, timeout ); 00522 } 00523 00524 KPassivePopup *KPassivePopup::message( int popupStyle, const TQString &caption, const TQString &text, 00525 const TQPixmap &icon, 00526 TQWidget *parent, const char *name, int timeout ) 00527 { 00528 KPassivePopup *pop = new KPassivePopup( popupStyle, parent, name ); 00529 pop->setAutoDelete( true ); 00530 pop->setView( caption, text, icon ); 00531 pop->hideDelay = timeout; 00532 pop->show(); 00533 00534 return pop; 00535 } 00536 00537 KPassivePopup *KPassivePopup::message( int popupStyle, const TQString &text, TQWidget *parent, const char *name ) 00538 { 00539 return message( popupStyle, TQString::null, text, TQPixmap(), parent, name ); 00540 } 00541 00542 KPassivePopup *KPassivePopup::message( int popupStyle, const TQString &caption, const TQString &text, 00543 TQWidget *parent, const char *name ) 00544 { 00545 return message( popupStyle, caption, text, TQPixmap(), parent, name ); 00546 } 00547 00548 KPassivePopup *KPassivePopup::message( int popupStyle, const TQString &caption, const TQString &text, 00549 const TQPixmap &icon, WId parent, const char *name, int timeout ) 00550 { 00551 KPassivePopup *pop = new KPassivePopup( popupStyle, parent, name ); 00552 pop->setAutoDelete( true ); 00553 pop->setView( caption, text, icon ); 00554 pop->hideDelay = timeout; 00555 pop->show(); 00556 00557 return pop; 00558 } 00559 00560 // Local Variables: 00561 // c-basic-offset: 4 00562 // End: