]> cloud.milkyroute.net Git - dolphin.git/blob - src/ktooltip.cpp
SVN_SILENT: increased version number for KDE 4.2
[dolphin.git] / src / ktooltip.cpp
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 ***************************************************************************/
19
20 #include "ktooltip.h"
21
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>
31
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
38
39 #include "ktooltip_p.h"
40
41
42
43 class KToolTipItemPrivate
44 {
45 public:
46 QMap<int, QVariant> map;
47 int type;
48 };
49
50 KToolTipItem::KToolTipItem(const QString &text, int type)
51 : d(new KToolTipItemPrivate)
52 {
53 d->map[Qt::DisplayRole] = text;
54 d->type = type;
55 }
56
57 KToolTipItem::KToolTipItem(const QIcon &icon, const QString &text, int type)
58 : d(new KToolTipItemPrivate)
59 {
60 d->map[Qt::DecorationRole] = icon;
61 d->map[Qt::DisplayRole] = text;
62 d->type = type;
63 }
64
65 KToolTipItem::~KToolTipItem()
66 {
67 delete d;
68 }
69
70 int KToolTipItem::type() const
71 {
72 return d->type;
73 }
74
75 QString KToolTipItem::text() const
76 {
77 return data(Qt::DisplayRole).toString();
78 }
79
80 QIcon KToolTipItem::icon() const
81 {
82 return qvariant_cast<QIcon>(data(Qt::DecorationRole));
83 }
84
85 QVariant KToolTipItem::data(int role) const
86 {
87 return d->map.value(role);
88 }
89
90 void KToolTipItem::setData(int role, const QVariant &data)
91 {
92 d->map[role] = data;
93 }
94
95
96
97 // ----------------------------------------------------------------------------
98
99
100 KStyleOptionToolTip::KStyleOptionToolTip()
101 : fontMetrics(QApplication::font())
102 {
103 }
104
105
106 // ----------------------------------------------------------------------------
107
108
109
110 KToolTipDelegate::KToolTipDelegate() : QObject()
111 {
112 }
113
114 KToolTipDelegate::~KToolTipDelegate()
115 {
116 }
117
118 QSize KToolTipDelegate::sizeHint(const KStyleOptionToolTip *option, const KToolTipItem *item) const
119 {
120 QSize size;
121 size.rwidth() = option->fontMetrics.width(item->text());
122 size.rheight() = option->fontMetrics.lineSpacing();
123
124 QIcon icon = item->icon();
125 if (!icon.isNull()) {
126 const QSize iconSize = icon.actualSize(option->decorationSize);
127 size.rwidth() += iconSize.width() + 4;
128 size.rheight() = qMax(size.height(), iconSize.height());
129 }
130
131 return size + QSize(20, 20);
132
133 }
134
135 void KToolTipDelegate::paint(QPainter *painter, const KStyleOptionToolTip *option,
136 const KToolTipItem *item) const
137 {
138 bool haveAlpha = haveAlphaChannel();
139 painter->setRenderHint(QPainter::Antialiasing);
140
141 QPainterPath path;
142 if (haveAlpha)
143 path.addRoundRect(option->rect.adjusted(0, 0, -1, -1), 25);
144 else
145 path.addRect(option->rect.adjusted(0, 0, -1, -1));
146
147 #if QT_VERSION >= 0x040400
148 QColor color = option->palette.color(QPalette::ToolTipBase);
149 #else
150 QColor color = option->palette.color(QPalette::Base);
151 #endif
152 QColor from = color.lighter(105);
153 QColor to = color.darker(120);
154
155 QLinearGradient gradient(0, 0, 0, 1);
156 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
157 gradient.setColorAt(0, from);
158 gradient.setColorAt(1, to);
159
160 painter->translate(.5, .5);
161 painter->setPen(QPen(Qt::black, 1));
162 painter->setBrush(gradient);
163 painter->drawPath(path);
164 painter->translate(-.5, -.5);
165
166 if (haveAlpha) {
167 QLinearGradient mask(0, 0, 0, 1);
168 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
169 gradient.setColorAt(0, QColor(0, 0, 0, 192));
170 gradient.setColorAt(1, QColor(0, 0, 0, 72));
171 painter->setCompositionMode(QPainter::CompositionMode_DestinationIn);
172 painter->fillRect(option->rect, gradient);
173 painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
174 }
175
176 QRect textRect = option->rect.adjusted(10, 10, -10, -10);
177
178 QIcon icon = item->icon();
179 if (!icon.isNull()) {
180 const QSize iconSize = icon.actualSize(option->decorationSize);
181 painter->drawPixmap(textRect.topLeft(), icon.pixmap(iconSize));
182 textRect.adjust(iconSize.width() + 4, 0, 0, 0);
183 }
184 painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, item->text());
185 }
186
187 QRegion KToolTipDelegate::inputShape(const KStyleOptionToolTip *option) const
188 {
189 return QRegion(option->rect);
190 }
191
192 QRegion KToolTipDelegate::shapeMask(const KStyleOptionToolTip *option) const
193 {
194 return QRegion(option->rect);
195 }
196
197 bool KToolTipDelegate::haveAlphaChannel() const
198 {
199 return KToolTipManager::instance()->haveAlphaChannel();
200 }
201
202
203
204 // ----------------------------------------------------------------------------
205
206
207
208 class KAbstractToolTipLabel
209 {
210 public:
211 KAbstractToolTipLabel() {}
212 virtual ~KAbstractToolTipLabel() {}
213
214 virtual void showTip(const QPoint &pos, const KToolTipItem *item) = 0;
215 virtual void moveTip(const QPoint &pos) = 0;
216 virtual void hideTip() = 0;
217
218 protected:
219 KStyleOptionToolTip styleOption() const;
220 KToolTipDelegate *delegate() const;
221 };
222
223 KStyleOptionToolTip KAbstractToolTipLabel::styleOption() const
224 {
225 KStyleOptionToolTip option;
226 KToolTipManager::instance()->initStyleOption(&option);
227 return option;
228 }
229
230 KToolTipDelegate *KAbstractToolTipLabel::delegate() const
231 {
232 return KToolTipManager::instance()->delegate();
233 }
234
235
236 // ----------------------------------------------------------------------------
237
238
239
240 class QWidgetLabel : public QWidget, public KAbstractToolTipLabel
241 {
242 public:
243 QWidgetLabel() : QWidget(0, Qt::ToolTip) {}
244 void showTip(const QPoint &pos, const KToolTipItem *item);
245 void moveTip(const QPoint &pos);
246 void hideTip();
247
248 private:
249 void paintEvent(QPaintEvent*);
250 QSize sizeHint() const;
251
252 private:
253 const KToolTipItem *currentItem;
254 };
255
256 void QWidgetLabel::showTip(const QPoint &pos, const KToolTipItem *item)
257 {
258 currentItem = item;
259 move(pos);
260 show();
261 }
262
263 void QWidgetLabel::hideTip()
264 {
265 hide();
266 currentItem = 0;
267 }
268
269 void QWidgetLabel::moveTip(const QPoint &pos)
270 {
271 move(pos);
272 }
273
274 void QWidgetLabel::paintEvent(QPaintEvent*)
275 {
276 KStyleOptionToolTip option = styleOption();
277 option.rect = rect();
278
279 setMask(delegate()->shapeMask(&option));
280
281 QPainter p(this);
282 p.setFont(option.font);
283 p.setPen(QPen(option.palette.brush(QPalette::Text), 0));
284 delegate()->paint(&p, &option, currentItem);
285 }
286
287 QSize QWidgetLabel::sizeHint() const
288 {
289 if (!currentItem)
290 return QSize();
291
292 KStyleOptionToolTip option = styleOption();
293 return delegate()->sizeHint(&option, currentItem);
294 }
295
296
297
298 // ----------------------------------------------------------------------------
299
300
301
302 #ifdef Q_WS_X11
303
304 // X11 specific label that displays the tip in an ARGB window.
305 class ArgbLabel : public KAbstractToolTipLabel
306 {
307 public:
308 ArgbLabel(Visual *visual, int depth);
309 ~ArgbLabel();
310
311 void showTip(const QPoint &pos, const KToolTipItem *item);
312 void moveTip(const QPoint &pos);
313 void hideTip();
314
315 private:
316 Window window;
317 Colormap colormap;
318 Picture picture;
319 bool mapped;
320 };
321
322 ArgbLabel::ArgbLabel(Visual *visual, int depth)
323 {
324 Display *dpy = QX11Info::display();
325 Window root = QX11Info::appRootWindow();
326 colormap = XCreateColormap(dpy, QX11Info::appRootWindow(), visual, AllocNone);
327
328 XSetWindowAttributes attr;
329 attr.border_pixel = 0;
330 attr.background_pixel = 0;
331 attr.colormap = colormap;
332 attr.override_redirect = True;
333
334 window = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, depth, InputOutput, visual,
335 CWBorderPixel | CWBackPixel | CWColormap |
336 CWOverrideRedirect, &attr);
337
338 // ### TODO: Set the WM hints so KWin can identify this window as a
339 // tooltip.
340
341 XRenderPictFormat *format = XRenderFindVisualFormat(dpy, visual);
342 picture = XRenderCreatePicture(dpy, window, format, 0, 0);
343
344 mapped = false;
345 }
346
347 ArgbLabel::~ArgbLabel()
348 {
349 Display *dpy = QX11Info::display();
350 XRenderFreePicture(dpy, picture);
351 XDestroyWindow(dpy, window);
352 XFreeColormap(dpy, colormap);
353 }
354
355 void ArgbLabel::showTip(const QPoint &pos, const KToolTipItem *item)
356 {
357 Display *dpy = QX11Info::display();
358 KStyleOptionToolTip option = styleOption();
359 const QSize size = delegate()->sizeHint(&option, item);
360 option.rect = QRect(QPoint(), size);
361
362 QPixmap pixmap(size);
363 pixmap.fill(Qt::transparent);
364
365 QPainter p(&pixmap);
366 p.setFont(option.font);
367 p.setPen(QPen(option.palette.brush(QPalette::Text), 0));
368 delegate()->paint(&p, &option, item);
369
370 // Resize, position and show the window.
371 XMoveResizeWindow(dpy, window, pos.x(), pos.y(), size.width(), size.height());
372
373 if (KToolTipManager::instance()->haveAlphaChannel()) {
374 const QRegion region = delegate()->inputShape(&option);
375 XShapeCombineRegion(dpy, window, ShapeInput, 0, 0, region.handle(), ShapeSet);
376 } else {
377 const QRegion region = delegate()->shapeMask(&option);
378 XShapeCombineRegion(dpy, window, ShapeBounding, 0, 0, region.handle(), ShapeSet);
379 }
380
381 XMapWindow(dpy, window);
382
383 // Blit the pixmap with the tip contents to the window.
384 // Since the window is override-redirect and an ARGB32 window,
385 // which always has an offscreen pixmap, there's no need to
386 // wait for an Expose event, or to process those.
387 XRenderComposite(dpy, PictOpSrc, pixmap.x11PictureHandle(), None,
388 picture, 0, 0, 0, 0, 0, 0, size.width(), size.height());
389
390 mapped = true;
391 }
392
393 void ArgbLabel::moveTip(const QPoint &pos)
394 {
395 if (mapped)
396 XMoveWindow(QX11Info::display(), window, pos.x(), pos.y());
397 }
398
399 void ArgbLabel::hideTip()
400 {
401 if (mapped) {
402 Display *dpy = QX11Info::display();
403 XUnmapWindow(dpy, window);
404 mapped = false;
405 }
406 }
407
408 #endif // Q_WS_X11
409
410
411
412
413 // ----------------------------------------------------------------------------
414
415
416
417
418 KToolTipManager *KToolTipManager::s_instance = 0;
419
420 KToolTipManager::KToolTipManager()
421 : label(0), currentItem(0), m_delegate(0)
422 {
423 #ifdef Q_WS_X11
424 Display *dpy = QX11Info::display();
425 int screen = DefaultScreen(dpy);
426 int depth = DefaultDepth(dpy, screen);
427 Visual *visual = DefaultVisual(dpy, screen);
428 net_wm_cm_s0 = XInternAtom(dpy, "_NET_WM_CM_S0", False);
429 haveArgbVisual = false;
430
431 int nvi;
432 XVisualInfo templ;
433 templ.screen = screen;
434 templ.depth = 32;
435 templ.c_class = TrueColor;
436 XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask |
437 VisualClassMask, &templ, &nvi);
438
439 for (int i = 0; i < nvi; ++i)
440 {
441 XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
442 if (format->type == PictTypeDirect && format->direct.alphaMask)
443 {
444 visual = xvi[i].visual;
445 depth = xvi[i].depth;
446 haveArgbVisual = true;
447 break;
448 }
449 }
450
451 if (haveArgbVisual)
452 label = new ArgbLabel(visual, depth);
453 else
454 #endif
455 label = new QWidgetLabel();
456 }
457
458 KToolTipManager::~KToolTipManager()
459 {
460 delete label;
461 delete currentItem;
462 }
463
464 void KToolTipManager::showTip(const QPoint &pos, KToolTipItem *item)
465 {
466 hideTip();
467 label->showTip(pos, item);
468 currentItem = item;
469 }
470
471 void KToolTipManager::hideTip()
472 {
473 label->hideTip();
474 delete currentItem;
475 currentItem = 0;
476 }
477
478 void KToolTipManager::initStyleOption(KStyleOptionToolTip *option) const
479 {
480 option->direction = QApplication::layoutDirection();
481 option->fontMetrics = QFontMetrics(QToolTip::font());
482 option->activeCorner = KStyleOptionToolTip::TopLeftCorner;
483 option->palette = QToolTip::palette();
484 option->font = QToolTip::font();
485 option->rect = QRect();
486 option->state = QStyle::State_None;
487 option->decorationSize = QSize(32, 32);
488 }
489
490 bool KToolTipManager::haveAlphaChannel() const
491 {
492 #ifdef Q_WS_X11
493 // ### This is a synchronous call - ideally we'd use a selection
494 // watcher to avoid it.
495 return haveArgbVisual &&
496 XGetSelectionOwner(QX11Info::display(), net_wm_cm_s0) != None;
497 #else
498 return false;
499 #endif
500 }
501
502 void KToolTipManager::setDelegate(KToolTipDelegate *delegate)
503 {
504 m_delegate = delegate;
505 }
506
507 KToolTipDelegate *KToolTipManager::delegate() const
508 {
509 return m_delegate;
510 }
511
512
513
514 // ----------------------------------------------------------------------------
515
516
517
518 namespace KToolTip
519 {
520 void showText(const QPoint &pos, const QString &text, QWidget *widget, const QRect &rect)
521 {
522 Q_UNUSED(widget)
523 Q_UNUSED(rect)
524 KToolTipItem *item = new KToolTipItem(text);
525 KToolTipManager::instance()->showTip(pos, item);
526 }
527
528 void showText(const QPoint &pos, const QString &text, QWidget *widget)
529 {
530 showText(pos, text, widget, QRect());
531 }
532
533 void showTip(const QPoint &pos, KToolTipItem *item)
534 {
535 KToolTipManager::instance()->showTip(pos, item);
536 }
537
538 void hideTip()
539 {
540 KToolTipManager::instance()->hideTip();
541 }
542
543 void setToolTipDelegate(KToolTipDelegate *delegate)
544 {
545 KToolTipManager::instance()->setDelegate(delegate);
546 }
547 }
548