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