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