fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kdeui / util / kpassivepopup.cpp
blob1b85194245da806ba34a55006e7851fea681b7a8
1 /*
2 * copyright : (C) 2001-2006 by Richard Moore
3 * copyright : (C) 2004-2005 by Sascha Cunz
4 * License : This file is released under the terms of the LGPL, version 2.
5 * email : rich@kde.org
6 * email : sascha.cunz@tiscali.de
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 #include "kpassivepopup.h"
24 #include "kpassivepopup.moc"
26 // Qt
27 #include <QApplication>
28 #include <QBitmap>
29 #include <QLabel>
30 #include <QLayout>
31 #include <QMouseEvent>
32 #include <QPainter>
33 #include <QPainterPath>
34 #include <QPolygonF>
35 #include <QTimer>
36 #include <QToolTip>
37 #include <QSystemTrayIcon>
39 #include <kvbox.h>
40 #include <kdebug.h>
41 #include <kdialog.h>
42 #include <kglobalsettings.h>
44 #include <kconfig.h>
46 #ifdef Q_WS_X11
47 #include <qx11info_x11.h>
48 #include <netwm.h>
49 #endif
51 #include <config.h>
53 static const int DEFAULT_POPUP_TYPE = KPassivePopup::Boxed;
54 static const int DEFAULT_POPUP_TIME = 6*1000;
55 static const Qt::WindowFlags POPUP_FLAGS = Qt::Tool | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint;
57 class KPassivePopup::Private
59 public:
60 Private()
61 : popupStyle( DEFAULT_POPUP_TYPE ),
62 msgView(0),
63 topLayout(0),
64 hideDelay( DEFAULT_POPUP_TIME ),
65 hideTimer(0),
66 autoDelete( false )
71 int popupStyle;
72 QPolygon surround;
73 QPoint anchor;
74 QPoint fixedPosition;
76 WId window;
77 QWidget *msgView;
78 QBoxLayout *topLayout;
79 int hideDelay;
80 QTimer *hideTimer;
82 QLabel *ttlIcon;
83 QLabel *ttl;
84 QLabel *msg;
86 bool autoDelete;
89 KPassivePopup::KPassivePopup( QWidget *parent, Qt::WFlags f )
90 : QFrame( 0, f ? f : POPUP_FLAGS ),
91 d(new Private())
93 init( parent ? parent->effectiveWinId() : 0L );
96 KPassivePopup::KPassivePopup( WId win )
97 : QFrame( 0 ),
98 d(new Private())
100 init( win );
103 #if 0 // These break macos and win32 where the definition of WId makes them ambiguous
104 KPassivePopup::KPassivePopup( int popupStyle, QWidget *parent, Qt::WFlags f )
105 : QFrame( 0, f ? f : POPUP_FLAGS ),
106 d(new Private())
108 init( parent ? parent->winId() : 0L );
109 setPopupStyle( popupStyle );
112 KPassivePopup::KPassivePopup( int popupStyle, WId win, Qt::WFlags f )
113 : QFrame( 0, f ? f : POPUP_FLAGS ),
114 d(new Private())
116 init( win );
117 setPopupStyle( popupStyle );
119 #endif
121 void KPassivePopup::init( WId window )
123 d->window = window;
124 d->hideTimer = new QTimer( this );
126 setWindowFlags( POPUP_FLAGS );
127 setFrameStyle( QFrame::Box| QFrame::Plain );
128 setLineWidth( 2 );
130 if( d->popupStyle == Boxed )
132 setFrameStyle( QFrame::Box| QFrame::Plain );
133 setLineWidth( 2 );
135 else if( d->popupStyle == Balloon )
137 setPalette(QToolTip::palette());
138 //XXX dead ? setAutoMask(true);
140 connect( d->hideTimer, SIGNAL( timeout() ), SLOT( hide() ) );
141 connect( this, SIGNAL( clicked() ), SLOT( hide() ) );
144 KPassivePopup::~KPassivePopup()
146 delete d;
149 void KPassivePopup::setPopupStyle( int popupstyle )
151 if ( d->popupStyle == popupstyle )
152 return;
154 d->popupStyle = popupstyle;
155 if( d->popupStyle == Boxed )
157 setFrameStyle( QFrame::Box| QFrame::Plain );
158 setLineWidth( 2 );
160 else if( d->popupStyle == Balloon )
162 setPalette(QToolTip::palette());
163 //XXX dead ? setAutoMask(true);
167 void KPassivePopup::setView( QWidget *child )
169 delete d->msgView;
170 d->msgView = child;
172 delete d->topLayout;
173 d->topLayout = new QVBoxLayout( this );
174 d->topLayout->setMargin( d->popupStyle == Balloon ? 22 : KDialog::marginHint() );
175 d->topLayout->setSpacing( KDialog::spacingHint() );
176 d->topLayout->addWidget( d->msgView );
177 d->topLayout->activate();
180 void KPassivePopup::setView( const QString &caption, const QString &text,
181 const QPixmap &icon )
183 // kDebug() << "KPassivePopup::setView " << caption << ", " << text;
184 setView( standardView( caption, text, icon, this ) );
188 KVBox * KPassivePopup::standardView( const QString& caption,
189 const QString& text,
190 const QPixmap& icon,
191 QWidget *parent )
193 KVBox *vb = new KVBox( parent ? parent : this );
194 vb->setSpacing( KDialog::spacingHint() );
196 KHBox *hb=0;
197 if ( !icon.isNull() ) {
198 hb = new KHBox( vb );
199 hb->setMargin( 0 );
200 hb->setSpacing( KDialog::spacingHint() );
201 d->ttlIcon = new QLabel( hb );
202 d->ttlIcon->setPixmap( icon );
203 d->ttlIcon->setAlignment( Qt::AlignLeft );
206 if ( !caption.isEmpty() ) {
207 d->ttl = new QLabel( caption, hb ? hb : vb );
208 QFont fnt = d->ttl->font();
209 fnt.setBold( true );
210 d->ttl->setFont( fnt );
211 d->ttl->setAlignment( Qt::AlignHCenter );
213 if ( hb )
214 hb->setStretchFactor( d->ttl, 10 ); // enforce centering
217 if ( !text.isEmpty() ) {
218 d->msg = new QLabel( text, vb );
219 d->msg->setAlignment( Qt::AlignLeft );
220 d->msg->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
221 d->msg->setOpenExternalLinks(true);
224 return vb;
227 void KPassivePopup::setView( const QString &caption, const QString &text )
229 setView( caption, text, QPixmap() );
232 QWidget *KPassivePopup::view() const
234 return d->msgView;
237 int KPassivePopup::timeout() const
239 return d->hideDelay;
242 void KPassivePopup::setTimeout( int delay )
244 d->hideDelay = delay;
245 if( d->hideTimer->isActive() )
247 if( delay ) {
248 d->hideTimer->start( delay );
249 } else {
250 d->hideTimer->stop();
255 bool KPassivePopup::autoDelete() const
257 return d->autoDelete;
260 void KPassivePopup::setAutoDelete( bool autoDelete )
262 d->autoDelete = autoDelete;
265 void KPassivePopup::mouseReleaseEvent( QMouseEvent *e )
267 emit clicked();
268 emit clicked( e->pos() );
272 // Main Implementation
275 void KPassivePopup::setVisible( bool visible )
277 if (! visible ) {
278 QFrame::setVisible( visible );
279 return;
282 if ( size() != sizeHint() )
283 resize( sizeHint() );
285 if ( d->fixedPosition.isNull() )
286 positionSelf();
287 else {
288 if( d->popupStyle == Balloon )
289 setAnchor( d->fixedPosition );
290 else
291 move( d->fixedPosition );
293 QFrame::setVisible( /*visible=*/ true );
295 int delay = d->hideDelay;
296 if ( delay < 0 ) {
297 delay = DEFAULT_POPUP_TIME;
300 if ( delay > 0 ) {
301 d->hideTimer->start( delay );
305 void KPassivePopup::show()
307 QFrame::show();
310 void KPassivePopup::show(const QPoint &p)
312 d->fixedPosition = p;
313 show();
316 void KPassivePopup::hideEvent( QHideEvent * )
318 d->hideTimer->stop();
319 if ( d->autoDelete )
320 deleteLater();
323 QRect KPassivePopup::defaultArea() const
325 #ifdef Q_WS_X11
326 NETRootInfo info( QX11Info::display(),
327 NET::NumberOfDesktops |
328 NET::CurrentDesktop |
329 NET::WorkArea,
330 -1, false );
331 info.activate();
332 NETRect workArea = info.workArea( info.currentDesktop() );
333 QRect r;
334 r.setRect( workArea.pos.x, workArea.pos.y, 0, 0 ); // top left
335 #else
336 // FIX IT
337 QRect r;
338 r.setRect( 100, 100, 200, 200 ); // top left
339 #endif
340 return r;
343 void KPassivePopup::positionSelf()
345 QRect target;
347 #ifdef Q_WS_X11
348 if ( !d->window ) {
349 target = defaultArea();
352 else {
353 NETWinInfo ni( QX11Info::display(), d->window, QX11Info::appRootWindow(),
354 NET::WMIconGeometry );
356 // Figure out where to put the popup. Note that we must handle
357 // windows that skip the taskbar cleanly
358 if ( ni.state() & NET::SkipTaskbar ) {
359 target = defaultArea();
361 else {
362 NETRect r = ni.iconGeometry();
363 target.setRect( r.pos.x, r.pos.y, r.size.width, r.size.height );
364 if ( target.isNull() ) { // bogus value, use the exact position
365 NETRect dummy;
366 ni.kdeGeometry( dummy, r );
367 target.setRect( r.pos.x, r.pos.y,
368 r.size.width, r.size.height);
372 #else
373 target = defaultArea();
374 #endif
375 moveNear( target );
378 void KPassivePopup::moveNear( const QRect &target )
380 QPoint pos = calculateNearbyPoint(target);
381 if( d->popupStyle == Balloon )
382 setAnchor( pos );
383 else
384 move( pos.x(), pos.y() );
387 QPoint KPassivePopup::calculateNearbyPoint( const QRect &target) {
388 QPoint pos = target.topLeft();
389 int x = pos.x();
390 int y = pos.y();
391 int w = minimumSizeHint().width();
392 int h = minimumSizeHint().height();
394 QRect r = KGlobalSettings::desktopGeometry(QPoint(x+w/2,y+h/2));
396 if( d->popupStyle == Balloon )
398 // find a point to anchor to
399 if( x + w > r.width() ){
400 x = x + target.width();
403 if( y + h > r.height() ){
404 y = y + target.height();
406 } else
408 if ( x < r.center().x() )
409 x = x + target.width();
410 else
411 x = x - w;
413 // It's apparently trying to go off screen, so display it ALL at the bottom.
414 if ( (y + h) > r.bottom() )
415 y = r.bottom() - h;
417 if ( (x + w) > r.right() )
418 x = r.right() - w;
420 if ( y < r.top() )
421 y = r.top();
423 if ( x < r.left() )
424 x = r.left();
426 return QPoint( x, y );
429 QPoint KPassivePopup::anchor() const
431 return d->anchor;
434 void KPassivePopup::setAnchor(const QPoint &anchor)
436 d->anchor = anchor;
437 updateMask();
440 void KPassivePopup::paintEvent( QPaintEvent* pe )
442 if( d->popupStyle == Balloon )
444 QPainter p;
445 p.begin( this );
446 p.drawPolygon( d->surround );
447 } else
448 QFrame::paintEvent( pe );
451 void KPassivePopup::updateMask()
453 // get screen-geometry for screen our anchor is on
454 // (geometry can differ from screen to screen!
455 QRect deskRect = KGlobalSettings::desktopGeometry(d->anchor);
457 int xh = 70, xl = 40;
458 if( width() < 80 )
459 xh = xl = 40;
460 else if( width() < 110 )
461 xh = width() - 40;
463 bool bottom = (d->anchor.y() + height()) > ((deskRect.y() + deskRect.height()-48));
464 bool right = (d->anchor.x() + width()) > ((deskRect.x() + deskRect.width()-48));
466 QPoint corners[4] = {
467 QPoint( width() - 50, 10 ),
468 QPoint( 10, 10 ),
469 QPoint( 10, height() - 50 ),
470 QPoint( width() - 50, height() - 50 )
473 QBitmap mask( width(), height() );
474 mask.clear();
475 QPainter p( &mask );
476 QBrush brush( Qt::color1, Qt::SolidPattern );
477 p.setBrush( brush );
479 int i = 0, z = 0;
480 for (; i < 4; ++i) {
481 QPainterPath path;
482 path.moveTo(corners[i].x(),corners[i].y());
483 path.arcTo(corners[i].x(),corners[i].y(),40,40, i * 90 , 90);
484 QPolygon corner = path.toFillPolygon().toPolygon();
486 d->surround.resize( z + corner.count() - 1 );
487 for (int s = 1; s < corner.count() - 1; s++, z++) {
488 d->surround.setPoint( z, corner[s] );
491 if (bottom && i == 2) {
492 if (right) {
493 d->surround.resize( z + 3 );
494 d->surround.setPoint( z++, QPoint( width() - xh, height() - 10 ) );
495 d->surround.setPoint( z++, QPoint( width() - 20, height() ) );
496 d->surround.setPoint( z++, QPoint( width() - xl, height() - 10 ) );
497 } else {
498 d->surround.resize( z + 3 );
499 d->surround.setPoint( z++, QPoint( xl, height() - 10 ) );
500 d->surround.setPoint( z++, QPoint( 20, height() ) );
501 d->surround.setPoint( z++, QPoint( xh, height() - 10 ) );
503 } else if (!bottom && i == 0) {
504 if (right) {
505 d->surround.resize( z + 3 );
506 d->surround.setPoint( z++, QPoint( width() - xl, 10 ) );
507 d->surround.setPoint( z++, QPoint( width() - 20, 0 ) );
508 d->surround.setPoint( z++, QPoint( width() - xh, 10 ) );
509 } else {
510 d->surround.resize( z + 3 );
511 d->surround.setPoint( z++, QPoint( xh, 10 ) );
512 d->surround.setPoint( z++, QPoint( 20, 0 ) );
513 d->surround.setPoint( z++, QPoint( xl, 10 ) );
518 d->surround.resize( z + 1 );
519 d->surround.setPoint( z, d->surround[0] );
520 p.drawPolygon( d->surround );
521 setMask(mask);
523 move( right ? d->anchor.x() - width() + 20 : ( d->anchor.x() < 11 ? 11 : d->anchor.x() - 20 ),
524 bottom ? d->anchor.y() - height() : ( d->anchor.y() < 11 ? 11 : d->anchor.y() ) );
526 update();
530 // Convenience Methods
533 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
534 const QPixmap &icon,
535 QWidget *parent, int timeout )
537 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
540 KPassivePopup *KPassivePopup::message( const QString &text, QWidget *parent )
542 return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
545 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
546 QWidget *parent )
548 return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
551 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
552 const QPixmap &icon, WId parent, int timeout )
554 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
557 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
558 const QPixmap &icon,
559 QSystemTrayIcon *parent, int timeout )
561 return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
564 KPassivePopup *KPassivePopup::message( const QString &text, QSystemTrayIcon *parent )
566 return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
569 KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
570 QSystemTrayIcon *parent )
572 return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
576 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
577 const QPixmap &icon,
578 QWidget *parent, int timeout )
580 KPassivePopup *pop = new KPassivePopup( parent );
581 pop->setPopupStyle( popupStyle );
582 pop->setAutoDelete( true );
583 pop->setView( caption, text, icon );
584 pop->d->hideDelay = timeout;
585 pop->show();
587 return pop;
590 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QWidget *parent )
592 return message( popupStyle, QString(), text, QPixmap(), parent );
595 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
596 QWidget *parent )
598 return message( popupStyle, caption, text, QPixmap(), parent );
601 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
602 const QPixmap &icon, WId parent, int timeout )
604 KPassivePopup *pop = new KPassivePopup( parent );
605 pop->setPopupStyle( popupStyle );
606 pop->setAutoDelete( true );
607 pop->setView( caption, text, icon );
608 pop->d->hideDelay = timeout;
609 pop->show();
611 return pop;
614 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
615 const QPixmap &icon,
616 QSystemTrayIcon *parent, int timeout )
618 KPassivePopup *pop = new KPassivePopup( );
619 pop->setPopupStyle( popupStyle );
620 pop->setAutoDelete( true );
621 pop->setView( caption, text, icon );
622 pop->d->hideDelay = timeout;
623 QPoint pos = pop->calculateNearbyPoint(parent->geometry());
624 pop->show(pos);
625 pop->moveNear(parent->geometry());
627 return pop;
630 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QSystemTrayIcon *parent )
632 return message( popupStyle, QString(), text, QPixmap(), parent );
635 KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
636 QSystemTrayIcon *parent )
638 return message( popupStyle, caption, text, QPixmap(), parent );
642 // Local Variables:
643 // c-basic-offset: 4
644 // End: