1 /***************************************************************************
2 * Copyright (C) 2008 by Fredrik Höglund <fredrik@kde.org> *
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. *
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. *
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 ***************************************************************************/
22 #include <QApplication>
34 # include <X11/Xlib.h>
35 # include <X11/extensions/Xrender.h>
36 # include <X11/extensions/shape.h>
39 #include "ktooltip_p.h"
42 // compile with XShape older than 1.0
44 const int ShapeInput
= 2;
48 class KToolTipItemPrivate
51 QMap
<int, QVariant
> map
;
55 KToolTipItem::KToolTipItem(const QString
&text
, int type
)
56 : d(new KToolTipItemPrivate
)
58 d
->map
[Qt::DisplayRole
] = text
;
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
;
70 KToolTipItem::~KToolTipItem()
75 int KToolTipItem::type() const
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
)
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
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
);
149 path
.addRoundRect(option
->rect
.adjusted(0, 0, -1, -1), 25);
151 path
.addRect(option
->rect
.adjusted(0, 0, -1, -1));
153 #if QT_VERSION >= 0x040400
154 QColor color
= option
->palette
.color(QPalette::ToolTipBase
);
156 QColor color
= option
->palette
.color(QPalette::Base
);
158 QColor from
= color
.lighter(105);
159 QColor to
= color
.darker(120);
161 QLinearGradient
gradient(0, 0, 0, 1);
162 gradient
.setCoordinateMode(QGradient::ObjectBoundingMode
);
163 gradient
.setColorAt(0, from
);
164 gradient
.setColorAt(1, to
);
166 painter
->translate(.5, .5);
167 painter
->setPen(QPen(Qt::black
, 1));
168 painter
->setBrush(gradient
);
169 painter
->drawPath(path
);
170 painter
->translate(-.5, -.5);
173 QLinearGradient
mask(0, 0, 0, 1);
174 gradient
.setCoordinateMode(QGradient::ObjectBoundingMode
);
175 gradient
.setColorAt(0, QColor(0, 0, 0, 192));
176 gradient
.setColorAt(1, QColor(0, 0, 0, 72));
177 painter
->setCompositionMode(QPainter::CompositionMode_DestinationIn
);
178 painter
->fillRect(option
->rect
, gradient
);
179 painter
->setCompositionMode(QPainter::CompositionMode_SourceOver
);
182 QRect textRect
= option
->rect
.adjusted(10, 10, -10, -10);
184 QIcon icon
= item
->icon();
185 if (!icon
.isNull()) {
186 const QSize iconSize
= icon
.actualSize(option
->decorationSize
);
187 painter
->drawPixmap(textRect
.topLeft(), icon
.pixmap(iconSize
));
188 textRect
.adjust(iconSize
.width() + 4, 0, 0, 0);
190 painter
->drawText(textRect
, Qt::AlignLeft
| Qt::AlignVCenter
, item
->text());
193 QRegion
KToolTipDelegate::inputShape(const KStyleOptionToolTip
*option
) const
195 return QRegion(option
->rect
);
198 QRegion
KToolTipDelegate::shapeMask(const KStyleOptionToolTip
*option
) const
200 return QRegion(option
->rect
);
203 bool KToolTipDelegate::haveAlphaChannel() const
205 return KToolTipManager::instance()->haveAlphaChannel();
210 // ----------------------------------------------------------------------------
214 class KAbstractToolTipLabel
217 KAbstractToolTipLabel() {}
218 virtual ~KAbstractToolTipLabel() {}
220 virtual void showTip(const QPoint
&pos
, const KToolTipItem
*item
) = 0;
221 virtual void moveTip(const QPoint
&pos
) = 0;
222 virtual void hideTip() = 0;
225 KStyleOptionToolTip
styleOption() const;
226 KToolTipDelegate
*delegate() const;
229 KStyleOptionToolTip
KAbstractToolTipLabel::styleOption() const
231 KStyleOptionToolTip option
;
232 KToolTipManager::instance()->initStyleOption(&option
);
236 KToolTipDelegate
*KAbstractToolTipLabel::delegate() const
238 return KToolTipManager::instance()->delegate();
242 // ----------------------------------------------------------------------------
246 class QWidgetLabel
: public QWidget
, public KAbstractToolTipLabel
249 QWidgetLabel() : QWidget(0, Qt::ToolTip
) {}
250 void showTip(const QPoint
&pos
, const KToolTipItem
*item
);
251 void moveTip(const QPoint
&pos
);
255 void paintEvent(QPaintEvent
*);
256 QSize
sizeHint() const;
259 const KToolTipItem
*currentItem
;
262 void QWidgetLabel::showTip(const QPoint
&pos
, const KToolTipItem
*item
)
269 void QWidgetLabel::hideTip()
275 void QWidgetLabel::moveTip(const QPoint
&pos
)
280 void QWidgetLabel::paintEvent(QPaintEvent
*)
282 KStyleOptionToolTip option
= styleOption();
283 option
.rect
= rect();
285 setMask(delegate()->shapeMask(&option
));
288 p
.setFont(option
.font
);
289 p
.setPen(QPen(option
.palette
.brush(QPalette::Text
), 0));
290 delegate()->paint(&p
, &option
, currentItem
);
293 QSize
QWidgetLabel::sizeHint() const
298 KStyleOptionToolTip option
= styleOption();
299 return delegate()->sizeHint(&option
, currentItem
);
304 // ----------------------------------------------------------------------------
310 // X11 specific label that displays the tip in an ARGB window.
311 class ArgbLabel
: public KAbstractToolTipLabel
314 ArgbLabel(Visual
*visual
, int depth
);
317 void showTip(const QPoint
&pos
, const KToolTipItem
*item
);
318 void moveTip(const QPoint
&pos
);
328 ArgbLabel::ArgbLabel(Visual
*visual
, int depth
)
330 Display
*dpy
= QX11Info::display();
331 Window root
= QX11Info::appRootWindow();
332 colormap
= XCreateColormap(dpy
, QX11Info::appRootWindow(), visual
, AllocNone
);
334 XSetWindowAttributes attr
;
335 attr
.border_pixel
= 0;
336 attr
.background_pixel
= 0;
337 attr
.colormap
= colormap
;
338 attr
.override_redirect
= True
;
340 window
= XCreateWindow(dpy
, root
, 0, 0, 1, 1, 0, depth
, InputOutput
, visual
,
341 CWBorderPixel
| CWBackPixel
| CWColormap
|
342 CWOverrideRedirect
, &attr
);
344 // ### TODO: Set the WM hints so KWin can identify this window as a
347 XRenderPictFormat
*format
= XRenderFindVisualFormat(dpy
, visual
);
348 picture
= XRenderCreatePicture(dpy
, window
, format
, 0, 0);
353 ArgbLabel::~ArgbLabel()
355 Display
*dpy
= QX11Info::display();
356 XRenderFreePicture(dpy
, picture
);
357 XDestroyWindow(dpy
, window
);
358 XFreeColormap(dpy
, colormap
);
361 void ArgbLabel::showTip(const QPoint
&pos
, const KToolTipItem
*item
)
363 Display
*dpy
= QX11Info::display();
364 KStyleOptionToolTip option
= styleOption();
365 const QSize size
= delegate()->sizeHint(&option
, item
);
366 option
.rect
= QRect(QPoint(), size
);
368 QPixmap
pixmap(size
);
369 pixmap
.fill(Qt::transparent
);
372 p
.setFont(option
.font
);
373 p
.setPen(QPen(option
.palette
.brush(QPalette::Text
), 0));
374 delegate()->paint(&p
, &option
, item
);
376 // Resize, position and show the window.
377 XMoveResizeWindow(dpy
, window
, pos
.x(), pos
.y(), size
.width(), size
.height());
379 if (KToolTipManager::instance()->haveAlphaChannel()) {
380 const QRegion region
= delegate()->inputShape(&option
);
381 XShapeCombineRegion(dpy
, window
, ShapeInput
, 0, 0, region
.handle(), ShapeSet
);
383 const QRegion region
= delegate()->shapeMask(&option
);
384 XShapeCombineRegion(dpy
, window
, ShapeBounding
, 0, 0, region
.handle(), ShapeSet
);
387 XMapWindow(dpy
, window
);
389 // Blit the pixmap with the tip contents to the window.
390 // Since the window is override-redirect and an ARGB32 window,
391 // which always has an offscreen pixmap, there's no need to
392 // wait for an Expose event, or to process those.
393 XRenderComposite(dpy
, PictOpSrc
, pixmap
.x11PictureHandle(), None
,
394 picture
, 0, 0, 0, 0, 0, 0, size
.width(), size
.height());
399 void ArgbLabel::moveTip(const QPoint
&pos
)
402 XMoveWindow(QX11Info::display(), window
, pos
.x(), pos
.y());
405 void ArgbLabel::hideTip()
408 Display
*dpy
= QX11Info::display();
409 XUnmapWindow(dpy
, window
);
419 // ----------------------------------------------------------------------------
424 KToolTipManager
*KToolTipManager::s_instance
= 0;
426 KToolTipManager::KToolTipManager()
427 : label(0), currentItem(0), m_delegate(0)
430 Display
*dpy
= QX11Info::display();
431 int screen
= DefaultScreen(dpy
);
432 int depth
= DefaultDepth(dpy
, screen
);
433 Visual
*visual
= DefaultVisual(dpy
, screen
);
434 net_wm_cm_s0
= XInternAtom(dpy
, "_NET_WM_CM_S0", False
);
435 haveArgbVisual
= false;
439 templ
.screen
= screen
;
441 templ
.c_class
= TrueColor
;
442 XVisualInfo
*xvi
= XGetVisualInfo(dpy
, VisualScreenMask
| VisualDepthMask
|
443 VisualClassMask
, &templ
, &nvi
);
445 for (int i
= 0; i
< nvi
; ++i
)
447 XRenderPictFormat
*format
= XRenderFindVisualFormat(dpy
, xvi
[i
].visual
);
448 if (format
->type
== PictTypeDirect
&& format
->direct
.alphaMask
)
450 visual
= xvi
[i
].visual
;
451 depth
= xvi
[i
].depth
;
452 haveArgbVisual
= true;
458 label
= new ArgbLabel(visual
, depth
);
461 label
= new QWidgetLabel();
464 KToolTipManager::~KToolTipManager()
470 void KToolTipManager::showTip(const QPoint
&pos
, KToolTipItem
*item
)
473 label
->showTip(pos
, item
);
478 void KToolTipManager::hideTip()
485 void KToolTipManager::initStyleOption(KStyleOptionToolTip
*option
) const
487 option
->direction
= QApplication::layoutDirection();
488 option
->fontMetrics
= QFontMetrics(QToolTip::font());
489 option
->activeCorner
= KStyleOptionToolTip::TopLeftCorner
;
490 option
->palette
= QToolTip::palette();
491 option
->font
= QToolTip::font();
492 option
->rect
= QRect();
493 option
->state
= QStyle::State_None
;
494 option
->decorationSize
= QSize(32, 32);
497 bool KToolTipManager::haveAlphaChannel() const
500 // ### This is a synchronous call - ideally we'd use a selection
501 // watcher to avoid it.
502 return haveArgbVisual
&&
503 XGetSelectionOwner(QX11Info::display(), net_wm_cm_s0
) != None
;
509 void KToolTipManager::setDelegate(KToolTipDelegate
*delegate
)
511 m_delegate
= delegate
;
514 void KToolTipManager::update()
516 if (currentItem
== 0)
518 label
->showTip(m_tooltipPos
, currentItem
);
521 KToolTipDelegate
*KToolTipManager::delegate() const
528 // ----------------------------------------------------------------------------
534 void showText(const QPoint
&pos
, const QString
&text
, QWidget
*widget
, const QRect
&rect
)
538 KToolTipItem
*item
= new KToolTipItem(text
);
539 KToolTipManager::instance()->showTip(pos
, item
);
542 void showText(const QPoint
&pos
, const QString
&text
, QWidget
*widget
)
544 showText(pos
, text
, widget
, QRect());
547 void showTip(const QPoint
&pos
, KToolTipItem
*item
)
549 KToolTipManager::instance()->showTip(pos
, item
);
554 KToolTipManager::instance()->hideTip();
557 void setToolTipDelegate(KToolTipDelegate
*delegate
)
559 KToolTipManager::instance()->setDelegate(delegate
);