add more spacing
[personal-kdebase.git] / apps / dolphin / src / tooltips / ktooltip.cpp
bloba0126fbffecdcc976660dd64b266aebcd3ab14ef
1 /***************************************************************************
2 * Copyright (C) 2008 by Fredrik Höglund <fredrik@kde.org> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
20 #include "ktooltip.h"
22 #include <QApplication>
23 #include <QMap>
24 #include <QPixmap>
25 #include <QPainter>
26 #include <QVariant>
27 #include <QIcon>
28 #include <QWidget>
29 #include <QToolTip>
30 #include <QDebug>
32 #ifdef Q_WS_X11
33 # include <QX11Info>
34 # include <X11/Xlib.h>
35 # include <X11/extensions/Xrender.h>
36 # include <X11/extensions/shape.h>
37 #endif
39 #include "ktooltip_p.h"
42 // compile with XShape older than 1.0
43 #ifndef ShapeInput
44 const int ShapeInput = 2;
45 #endif
48 class KToolTipItemPrivate
50 public:
51 QMap<int, QVariant> map;
52 int type;
55 KToolTipItem::KToolTipItem(const QString &text, int type)
56 : d(new KToolTipItemPrivate)
58 d->map[Qt::DisplayRole] = text;
59 d->type = type;
62 KToolTipItem::KToolTipItem(const QIcon &icon, const QString &text, int type)
63 : d(new KToolTipItemPrivate)
65 d->map[Qt::DecorationRole] = icon;
66 d->map[Qt::DisplayRole] = text;
67 d->type = type;
70 KToolTipItem::~KToolTipItem()
72 delete d;
75 int KToolTipItem::type() const
77 return d->type;
80 QString KToolTipItem::text() const
82 return data(Qt::DisplayRole).toString();
85 QIcon KToolTipItem::icon() const
87 return qvariant_cast<QIcon>(data(Qt::DecorationRole));
90 QVariant KToolTipItem::data(int role) const
92 return d->map.value(role);
95 void KToolTipItem::setData(int role, const QVariant &data)
97 d->map[role] = data;
98 KToolTipManager::instance()->update();
103 // ----------------------------------------------------------------------------
106 KStyleOptionToolTip::KStyleOptionToolTip()
107 : fontMetrics(QApplication::font())
112 // ----------------------------------------------------------------------------
116 KToolTipDelegate::KToolTipDelegate() : QObject()
120 KToolTipDelegate::~KToolTipDelegate()
124 QSize KToolTipDelegate::sizeHint(const KStyleOptionToolTip *option, const KToolTipItem *item) const
126 QSize size;
127 size.rwidth() = option->fontMetrics.width(item->text());
128 size.rheight() = option->fontMetrics.lineSpacing();
130 QIcon icon = item->icon();
131 if (!icon.isNull()) {
132 const QSize iconSize = icon.actualSize(option->decorationSize);
133 size.rwidth() += iconSize.width() + 4;
134 size.rheight() = qMax(size.height(), iconSize.height());
137 return size + QSize(20, 20);
141 void KToolTipDelegate::paint(QPainter *painter, const KStyleOptionToolTip *option,
142 const KToolTipItem *item) const
144 bool haveAlpha = haveAlphaChannel();
145 painter->setRenderHint(QPainter::Antialiasing);
147 QPainterPath path;
148 if (haveAlpha)
149 path.addRoundRect(option->rect.adjusted(0, 0, -1, -1), 25);
150 else
151 path.addRect(option->rect.adjusted(0, 0, -1, -1));
153 QColor color = option->palette.color(QPalette::ToolTipBase);
154 QColor from = color.lighter(105);
155 QColor to = color.darker(120);
157 QLinearGradient gradient(0, 0, 0, 1);
158 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
159 gradient.setColorAt(0, from);
160 gradient.setColorAt(1, to);
162 painter->translate(.5, .5);
163 painter->setPen(QPen(Qt::black, 1));
164 painter->setBrush(gradient);
165 painter->drawPath(path);
166 painter->translate(-.5, -.5);
168 if (haveAlpha) {
169 QLinearGradient mask(0, 0, 0, 1);
170 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
171 gradient.setColorAt(0, QColor(0, 0, 0, 192));
172 gradient.setColorAt(1, QColor(0, 0, 0, 72));
173 painter->setCompositionMode(QPainter::CompositionMode_DestinationIn);
174 painter->fillRect(option->rect, gradient);
175 painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
178 QRect textRect = option->rect.adjusted(10, 10, -10, -10);
180 QIcon icon = item->icon();
181 if (!icon.isNull()) {
182 const QSize iconSize = icon.actualSize(option->decorationSize);
183 painter->drawPixmap(textRect.topLeft(), icon.pixmap(iconSize));
184 textRect.adjust(iconSize.width() + 4, 0, 0, 0);
186 painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, item->text());
189 QRegion KToolTipDelegate::inputShape(const KStyleOptionToolTip *option) const
191 return QRegion(option->rect);
194 QRegion KToolTipDelegate::shapeMask(const KStyleOptionToolTip *option) const
196 return QRegion(option->rect);
199 bool KToolTipDelegate::haveAlphaChannel() const
201 return KToolTipManager::instance()->haveAlphaChannel();
206 // ----------------------------------------------------------------------------
210 class KAbstractToolTipLabel
212 public:
213 KAbstractToolTipLabel() {}
214 virtual ~KAbstractToolTipLabel() {}
216 virtual void showTip(const QPoint &pos, const KToolTipItem *item) = 0;
217 virtual void moveTip(const QPoint &pos) = 0;
218 virtual void hideTip() = 0;
220 protected:
221 KStyleOptionToolTip styleOption() const;
222 KToolTipDelegate *delegate() const;
225 KStyleOptionToolTip KAbstractToolTipLabel::styleOption() const
227 KStyleOptionToolTip option;
228 KToolTipManager::instance()->initStyleOption(&option);
229 return option;
232 KToolTipDelegate *KAbstractToolTipLabel::delegate() const
234 return KToolTipManager::instance()->delegate();
238 // ----------------------------------------------------------------------------
242 class QWidgetLabel : public QWidget, public KAbstractToolTipLabel
244 public:
245 QWidgetLabel() : QWidget(0, Qt::ToolTip) {}
246 void showTip(const QPoint &pos, const KToolTipItem *item);
247 void moveTip(const QPoint &pos);
248 void hideTip();
250 private:
251 void paintEvent(QPaintEvent*);
252 QSize sizeHint() const;
254 private:
255 const KToolTipItem *currentItem;
258 void QWidgetLabel::showTip(const QPoint &pos, const KToolTipItem *item)
260 currentItem = item;
261 move(pos);
262 show();
265 void QWidgetLabel::hideTip()
267 hide();
268 currentItem = 0;
271 void QWidgetLabel::moveTip(const QPoint &pos)
273 move(pos);
276 void QWidgetLabel::paintEvent(QPaintEvent*)
278 KStyleOptionToolTip option = styleOption();
279 option.rect = rect();
281 setMask(delegate()->shapeMask(&option));
283 QPainter p(this);
284 p.setFont(option.font);
285 p.setPen(QPen(option.palette.brush(QPalette::Text), 0));
286 delegate()->paint(&p, &option, currentItem);
289 QSize QWidgetLabel::sizeHint() const
291 if (!currentItem)
292 return QSize();
294 KStyleOptionToolTip option = styleOption();
295 return delegate()->sizeHint(&option, currentItem);
300 // ----------------------------------------------------------------------------
304 #ifdef Q_WS_X11
306 // X11 specific label that displays the tip in an ARGB window.
307 class ArgbLabel : public KAbstractToolTipLabel
309 public:
310 ArgbLabel(Visual *visual, int depth);
311 ~ArgbLabel();
313 void showTip(const QPoint &pos, const KToolTipItem *item);
314 void moveTip(const QPoint &pos);
315 void hideTip();
317 private:
318 Window window;
319 Colormap colormap;
320 Picture picture;
321 bool mapped;
324 ArgbLabel::ArgbLabel(Visual *visual, int depth)
326 Display *dpy = QX11Info::display();
327 Window root = QX11Info::appRootWindow();
328 colormap = XCreateColormap(dpy, QX11Info::appRootWindow(), visual, AllocNone);
330 XSetWindowAttributes attr;
331 attr.border_pixel = 0;
332 attr.background_pixel = 0;
333 attr.colormap = colormap;
334 attr.override_redirect = True;
336 window = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, depth, InputOutput, visual,
337 CWBorderPixel | CWBackPixel | CWColormap |
338 CWOverrideRedirect, &attr);
340 // ### TODO: Set the WM hints so KWin can identify this window as a
341 // tooltip.
343 XRenderPictFormat *format = XRenderFindVisualFormat(dpy, visual);
344 picture = XRenderCreatePicture(dpy, window, format, 0, 0);
346 mapped = false;
349 ArgbLabel::~ArgbLabel()
351 Display *dpy = QX11Info::display();
352 XRenderFreePicture(dpy, picture);
353 XDestroyWindow(dpy, window);
354 XFreeColormap(dpy, colormap);
357 void ArgbLabel::showTip(const QPoint &pos, const KToolTipItem *item)
359 Display *dpy = QX11Info::display();
360 KStyleOptionToolTip option = styleOption();
361 const QSize size = delegate()->sizeHint(&option, item);
362 option.rect = QRect(QPoint(), size);
364 QPixmap pixmap(size);
365 pixmap.fill(Qt::transparent);
367 QPainter p(&pixmap);
368 p.setFont(option.font);
369 p.setPen(QPen(option.palette.brush(QPalette::Text), 0));
370 delegate()->paint(&p, &option, item);
372 // Resize, position and show the window.
373 XMoveResizeWindow(dpy, window, pos.x(), pos.y(), size.width(), size.height());
375 if (KToolTipManager::instance()->haveAlphaChannel()) {
376 const QRegion region = delegate()->inputShape(&option);
377 XShapeCombineRegion(dpy, window, ShapeInput, 0, 0, region.handle(), ShapeSet);
378 } else {
379 const QRegion region = delegate()->shapeMask(&option);
380 XShapeCombineRegion(dpy, window, ShapeBounding, 0, 0, region.handle(), ShapeSet);
383 XMapWindow(dpy, window);
385 // Blit the pixmap with the tip contents to the window.
386 // Since the window is override-redirect and an ARGB32 window,
387 // which always has an offscreen pixmap, there's no need to
388 // wait for an Expose event, or to process those.
389 XRenderComposite(dpy, PictOpSrc, pixmap.x11PictureHandle(), None,
390 picture, 0, 0, 0, 0, 0, 0, size.width(), size.height());
392 mapped = true;
395 void ArgbLabel::moveTip(const QPoint &pos)
397 if (mapped)
398 XMoveWindow(QX11Info::display(), window, pos.x(), pos.y());
401 void ArgbLabel::hideTip()
403 if (mapped) {
404 Display *dpy = QX11Info::display();
405 XUnmapWindow(dpy, window);
406 mapped = false;
410 #endif // Q_WS_X11
415 // ----------------------------------------------------------------------------
420 KToolTipManager *KToolTipManager::s_instance = 0;
422 KToolTipManager::KToolTipManager()
423 : label(0), currentItem(0), m_delegate(0)
425 #ifdef Q_WS_X11
426 Display *dpy = QX11Info::display();
427 int screen = DefaultScreen(dpy);
428 int depth = DefaultDepth(dpy, screen);
429 Visual *visual = DefaultVisual(dpy, screen);
430 net_wm_cm_s0 = XInternAtom(dpy, "_NET_WM_CM_S0", False);
431 haveArgbVisual = false;
433 int nvi;
434 XVisualInfo templ;
435 templ.screen = screen;
436 templ.depth = 32;
437 templ.c_class = TrueColor;
438 XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask |
439 VisualClassMask, &templ, &nvi);
441 for (int i = 0; i < nvi; ++i)
443 XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
444 if (format->type == PictTypeDirect && format->direct.alphaMask)
446 visual = xvi[i].visual;
447 depth = xvi[i].depth;
448 haveArgbVisual = true;
449 break;
453 if (haveArgbVisual)
454 label = new ArgbLabel(visual, depth);
455 else
456 #endif
457 label = new QWidgetLabel();
460 KToolTipManager::~KToolTipManager()
462 delete label;
463 delete currentItem;
466 void KToolTipManager::showTip(const QPoint &pos, KToolTipItem *item)
468 hideTip();
469 label->showTip(pos, item);
470 currentItem = item;
471 m_tooltipPos = pos;
474 void KToolTipManager::hideTip()
476 label->hideTip();
477 delete currentItem;
478 currentItem = 0;
481 void KToolTipManager::initStyleOption(KStyleOptionToolTip *option) const
483 option->direction = QApplication::layoutDirection();
484 option->fontMetrics = QFontMetrics(QToolTip::font());
485 option->activeCorner = KStyleOptionToolTip::TopLeftCorner;
486 option->palette = QToolTip::palette();
487 option->font = QToolTip::font();
488 option->rect = QRect();
489 option->state = QStyle::State_None;
490 option->decorationSize = QSize(32, 32);
493 bool KToolTipManager::haveAlphaChannel() const
495 #ifdef Q_WS_X11
496 // ### This is a synchronous call - ideally we'd use a selection
497 // watcher to avoid it.
498 return haveArgbVisual &&
499 XGetSelectionOwner(QX11Info::display(), net_wm_cm_s0) != None;
500 #else
501 return false;
502 #endif
505 void KToolTipManager::setDelegate(KToolTipDelegate *delegate)
507 m_delegate = delegate;
510 void KToolTipManager::update()
512 if (currentItem == 0)
513 return;
514 label->showTip(m_tooltipPos, currentItem);
517 KToolTipDelegate *KToolTipManager::delegate() const
519 return m_delegate;
524 // ----------------------------------------------------------------------------
528 namespace KToolTip
530 void showText(const QPoint &pos, const QString &text, QWidget *widget, const QRect &rect)
532 Q_UNUSED(widget)
533 Q_UNUSED(rect)
534 KToolTipItem *item = new KToolTipItem(text);
535 KToolTipManager::instance()->showTip(pos, item);
538 void showText(const QPoint &pos, const QString &text, QWidget *widget)
540 showText(pos, text, widget, QRect());
543 void showTip(const QPoint &pos, KToolTipItem *item)
545 KToolTipManager::instance()->showTip(pos, item);
548 void hideTip()
550 KToolTipManager::instance()->hideTip();
553 void setToolTipDelegate(KToolTipDelegate *delegate)
555 KToolTipManager::instance()->setDelegate(delegate);