Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / libs / plasma / widgets / tooltip.cpp
blobb15e0a540ceb3caa837b938a0a287f127a4a2573
1 /*
2 * Copyright 2007 by Dan Meltzer <hydrogen@notyetimplemented.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2, or
7 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "tooltip_p.h"
22 #include <QBitmap>
23 #include <QHBoxLayout>
24 #include <QLabel>
25 #include <QMouseEvent>
26 #include <QPixmap>
27 #include <QTimer>
28 #include <QGraphicsView>
29 #ifdef Q_WS_X11
30 #include <QX11Info>
31 #endif
33 #include <KDebug>
34 #include <KGlobal>
35 #include <KWindowSystem>
36 #include <plasma/theme.h>
37 #include <plasma/svgpanel.h>
39 #ifdef Q_WS_X11
40 #include <X11/Xlib.h>
41 #include <fixx11h.h>
42 #endif
44 #include "plasma/plasma.h"
46 namespace Plasma {
48 class ToolTip::Private
50 public:
51 Private()
52 : label(0)
53 , imageLabel(0)
54 , preview(0)
55 , windowToPreview(0)
56 , currentWidget(0)
57 , isShown(false)
58 , delayedHide(false)
59 , showTimer(0)
60 , hideTimer(0)
61 { }
63 QLabel *label;
64 QLabel *imageLabel;
65 WindowPreview *preview;
66 WId windowToPreview;
67 Plasma::Widget *currentWidget;
68 bool isShown;
69 bool delayedHide;
70 QTimer *showTimer;
71 QTimer *hideTimer;
73 SvgPanel *background;
77 class ToolTipSingleton
79 public:
80 ToolTip self;
82 K_GLOBAL_STATIC( ToolTipSingleton, privateInstance )
84 ToolTip *ToolTip::self()
86 return &privateInstance->self;
89 void ToolTip::show(Plasma::Widget *widget)
91 d->hideTimer->stop();
92 d->delayedHide = false;
93 d->currentWidget = widget;
94 d->showTimer->stop();
96 if (d->isShown) {
97 // small delay to prevent unecessary showing when the mouse is moving quickly across items
98 // which can be too much for less powerful CPUs to keep up with
99 d->showTimer->start(200);
100 } else {
101 d->showTimer->start(500);
105 void ToolTip::delayedHide()
107 d->showTimer->stop(); // stop the timer to show the tooltip
108 d->delayedHide = true;
109 d->hideTimer->start(250);
112 void ToolTip::hide()
114 d->currentWidget = 0;
115 d->showTimer->stop(); //Mouse out, stop the timer to show the tooltip
116 d->delayedHide = false;
117 setVisible(false);
118 d->hideTimer->start(250); //250 ms delay before we are officially "gone" to allow for the time to move between widgets
121 Plasma::Widget *ToolTip::currentWidget() const
123 return d->currentWidget;
126 //PRIVATE FUNCTIONS
127 void ToolTip::showToolTip()
129 if (!d->currentWidget || !d->currentWidget->toolTip()) {
130 return;
133 QGraphicsView *v = d->currentWidget->view();
134 if (v && v->mouseGrabber()) {
135 return;
138 setData(*d->currentWidget->toolTip());
140 if( d->windowToPreview != 0 ) {
141 // show/hide the preview area
142 d->preview->show();
143 } else {
144 d->preview->hide();
146 layout()->activate();
148 resize(sizeHint());
149 move(d->currentWidget->popupPosition(size()));
151 if (isVisible()) {
152 d->preview->setInfo();
153 } else {
154 setVisible(true);
157 d->isShown = true; //ToolTip is visible
160 void ToolTip::resetShownState()
162 if (!isVisible() || //One might have moused out and back in again
163 d->delayedHide) {
164 d->delayedHide = false;
165 d->isShown = false;
166 d->currentWidget = 0;
167 setVisible(false);
171 void ToolTip::showEvent(QShowEvent *e)
173 QWidget::showEvent(e);
174 d->preview->setInfo();
177 void ToolTip::mouseReleaseEvent(QMouseEvent* event)
179 if (rect().contains(event->pos())) {
180 hide();
184 ToolTip::ToolTip()
185 : QWidget(0)
186 , d( new Private )
188 setWindowFlags(Qt::ToolTip);
189 QGridLayout *l = new QGridLayout;
190 d->preview = new WindowPreview;
191 d->label = new QLabel;
192 d->label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
193 d->label->setWordWrap(true);
194 d->imageLabel = new QLabel;
195 d->imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
197 d->background = new SvgPanel("widgets/tooltip", this);
198 d->background->setBorderFlags(SvgPanel::DrawAllBorders);
200 connect(d->background, SIGNAL(repaintNeeded()), this, SLOT(update()));
202 l->addWidget(d->preview, 0, 0, 1, 2);
203 l->addWidget(d->imageLabel, 1, 0);
204 l->addWidget(d->label, 1, 1);
205 setLayout(l);
207 d->showTimer = new QTimer(this);
208 d->showTimer->setSingleShot(true);
209 d->hideTimer = new QTimer(this);
210 d->hideTimer->setSingleShot(true);
212 connect(d->showTimer, SIGNAL(timeout()), SLOT(showToolTip()));
213 connect(d->hideTimer, SIGNAL(timeout()), SLOT(resetShownState()));
215 connect(Plasma::Theme::self(), SIGNAL(changed()), this, SLOT(themeUpdated()));
216 themeUpdated();
219 ToolTip::~ToolTip()
221 delete d;
224 void ToolTip::setData(const Plasma::ToolTipData &data)
226 //reset our size
227 d->label->setText("<qt><b>" + data.mainText + "</b><br>" + data.subText + "</qt>");
228 d->imageLabel->setPixmap(data.image);
229 d->windowToPreview = data.windowToPreview;
230 d->preview->setWindowId( d->windowToPreview );
232 if (isVisible()) {
233 resize(sizeHint());
235 if (d->currentWidget) {
236 move(d->currentWidget->popupPosition(size()));
241 void ToolTip::themeUpdated()
243 const int topHeight = d->background->marginSize(Plasma::TopMargin);
244 const int leftWidth = d->background->marginSize(Plasma::LeftMargin);
245 const int rightWidth = d->background->marginSize(Plasma::RightMargin);
246 const int bottomHeight = d->background->marginSize(Plasma::BottomMargin);
247 setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight);
249 // Make the tooltip use Plasma's colorscheme
250 QPalette plasmaPalette = QPalette();
251 plasmaPalette.setColor(QPalette::Window, Plasma::Theme::self()->backgroundColor());
252 plasmaPalette.setColor(QPalette::WindowText, Plasma::Theme::self()->textColor());
253 setAutoFillBackground(true);
254 setPalette(plasmaPalette);
257 // A widget which reserves area for window preview and sets hints on the toplevel
258 // tooltip widget that tells KWin to render the preview in this area. This depends
259 // on KWin's TaskbarThumbnail compositing effect (which is here detected).
261 void WindowPreview::setWindowId(WId w)
263 if (!previewsAvailable()) {
264 id = 0;
265 return;
267 id = w;
268 readWindowSize();
271 bool WindowPreview::previewsAvailable() const
273 if (!KWindowSystem::compositingActive()) {
274 return false;
276 #ifdef Q_WS_X11
277 // hackish way to find out if KWin has the effect enabled,
278 // TODO provide proper support
279 Display* dpy = QX11Info::display();
280 Atom atom = XInternAtom(dpy, "_KDE_WINDOW_PREVIEW", False);
281 int cnt;
282 Atom* list = XListProperties(dpy, DefaultRootWindow( dpy ), &cnt);
283 if (list != NULL) {
284 bool ret = ( qFind(list, list + cnt, atom) != list + cnt );
285 XFree(list);
286 return ret;
288 #endif
289 return false;
292 QSize WindowPreview::sizeHint() const
294 if (id == 0) {
295 return QSize();
297 if (!windowSize.isValid()) {
298 readWindowSize();
300 QSize s = windowSize;
301 s.scale(200, 150, Qt::KeepAspectRatio);
302 return s;
305 void WindowPreview::readWindowSize() const
307 #ifdef Q_WS_X11
308 Window r;
309 int x, y;
310 unsigned int w, h, b, d;
311 if (XGetGeometry(QX11Info::display(), id, &r, &x, &y, &w, &h, &b, &d)) {
312 windowSize = QSize( w, h );
313 } else {
314 windowSize = QSize();
316 #else
317 windowSize = QSize();
318 #endif
321 void WindowPreview::setInfo()
323 #ifdef Q_WS_X11
324 Display *dpy = QX11Info::display();
325 Atom atom = XInternAtom(dpy, "_KDE_WINDOW_PREVIEW", False);
326 if (id == 0) {
327 XDeleteProperty(dpy, parentWidget()->winId(), atom);
328 return;
330 if (!windowSize.isValid()) {
331 readWindowSize();
333 if (!windowSize.isValid()) {
334 XDeleteProperty(dpy, parentWidget()->winId(), atom);
335 return;
337 Q_ASSERT( parentWidget()->isWindow()); // parent must be toplevel
338 long data[] = { 1, 5, id, x(), y(), width(), height() };
339 XChangeProperty(dpy, parentWidget()->winId(), atom, atom, 32, PropModeReplace,
340 reinterpret_cast< unsigned char* >( data ), sizeof( data ) / sizeof( data[ 0 ] ));
341 #endif
344 void ToolTip::resizeEvent(QResizeEvent *e)
346 QWidget::resizeEvent(e);
347 d->background->resize(size());
349 if (KWindowSystem::compositingActive()) {
350 return;
353 QBitmap mask(width(), height());
354 QPainter maskPainter(&mask);
356 mask.fill(Qt::white);
358 maskPainter.setBrush(Qt::black);
359 maskPainter.setPen(Qt::black);
361 maskPainter.drawPath(roundedRectangle(mask.rect().adjusted(-1,-1,-1,-1), 10));
362 setMask(mask);
365 void ToolTip::paintEvent(QPaintEvent *e)
367 QPainter painter(this);
368 painter.setRenderHint(QPainter::Antialiasing);
369 painter.setClipRect(e->rect());
370 painter.setCompositionMode(QPainter::CompositionMode_Source );
371 painter.fillRect(rect(), Qt::transparent);
373 d->background->paint(&painter, rect());
375 //Stroke border if there is no compositing
376 if (!KWindowSystem::compositingActive()) {
377 painter.setCompositionMode(QPainter::CompositionMode_SourceOver );
378 painter.setPen(Plasma::Theme::self()->textColor());
379 painter.drawPath(roundedRectangle(rect().adjusted(.5,.5,-.5,-.5), 10));
384 #include "tooltip_p.moc"