2 * Copyright 2007 by Dan Meltzer <hydrogen@notyetimplemented.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2, or
7 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "tooltip_p.h"
23 #include <QHBoxLayout>
25 #include <QMouseEvent>
28 #include <QGraphicsView>
35 #include <KWindowSystem>
36 #include <plasma/theme.h>
37 #include <plasma/svgpanel.h>
44 #include "plasma/plasma.h"
48 class ToolTip::Private
65 WindowPreview
*preview
;
67 Plasma::Widget
*currentWidget
;
77 class ToolTipSingleton
82 K_GLOBAL_STATIC( ToolTipSingleton
, privateInstance
)
84 ToolTip
*ToolTip::self()
86 return &privateInstance
->self
;
89 void ToolTip::show(Plasma::Widget
*widget
)
92 d
->delayedHide
= false;
93 d
->currentWidget
= widget
;
97 // small delay to prevent unecessary showing when the mouse is moving quickly across items
98 // which can be too much for less powerful CPUs to keep up with
99 d
->showTimer
->start(200);
101 d
->showTimer
->start(500);
105 void ToolTip::delayedHide()
107 d
->showTimer
->stop(); // stop the timer to show the tooltip
108 d
->delayedHide
= true;
109 d
->hideTimer
->start(250);
114 d
->currentWidget
= 0;
115 d
->showTimer
->stop(); //Mouse out, stop the timer to show the tooltip
116 d
->delayedHide
= false;
118 d
->hideTimer
->start(250); //250 ms delay before we are officially "gone" to allow for the time to move between widgets
121 Plasma::Widget
*ToolTip::currentWidget() const
123 return d
->currentWidget
;
127 void ToolTip::showToolTip()
129 if (!d
->currentWidget
|| !d
->currentWidget
->toolTip()) {
133 QGraphicsView
*v
= d
->currentWidget
->view();
134 if (v
&& v
->mouseGrabber()) {
138 setData(*d
->currentWidget
->toolTip());
140 if( d
->windowToPreview
!= 0 ) {
141 // show/hide the preview area
146 layout()->activate();
149 move(d
->currentWidget
->popupPosition(size()));
152 d
->preview
->setInfo();
157 d
->isShown
= true; //ToolTip is visible
160 void ToolTip::resetShownState()
162 if (!isVisible() || //One might have moused out and back in again
164 d
->delayedHide
= false;
166 d
->currentWidget
= 0;
171 void ToolTip::showEvent(QShowEvent
*e
)
173 QWidget::showEvent(e
);
174 d
->preview
->setInfo();
177 void ToolTip::mouseReleaseEvent(QMouseEvent
* event
)
179 if (rect().contains(event
->pos())) {
188 setWindowFlags(Qt::ToolTip
);
189 QGridLayout
*l
= new QGridLayout
;
190 d
->preview
= new WindowPreview
;
191 d
->label
= new QLabel
;
192 d
->label
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Minimum
);
193 d
->label
->setWordWrap(true);
194 d
->imageLabel
= new QLabel
;
195 d
->imageLabel
->setAlignment(Qt::AlignTop
| Qt::AlignLeft
);
197 d
->background
= new SvgPanel("widgets/tooltip", this);
198 d
->background
->setBorderFlags(SvgPanel::DrawAllBorders
);
200 connect(d
->background
, SIGNAL(repaintNeeded()), this, SLOT(update()));
202 l
->addWidget(d
->preview
, 0, 0, 1, 2);
203 l
->addWidget(d
->imageLabel
, 1, 0);
204 l
->addWidget(d
->label
, 1, 1);
207 d
->showTimer
= new QTimer(this);
208 d
->showTimer
->setSingleShot(true);
209 d
->hideTimer
= new QTimer(this);
210 d
->hideTimer
->setSingleShot(true);
212 connect(d
->showTimer
, SIGNAL(timeout()), SLOT(showToolTip()));
213 connect(d
->hideTimer
, SIGNAL(timeout()), SLOT(resetShownState()));
215 connect(Plasma::Theme::self(), SIGNAL(changed()), this, SLOT(themeUpdated()));
224 void ToolTip::setData(const Plasma::ToolTipData
&data
)
227 d
->label
->setText("<qt><b>" + data
.mainText
+ "</b><br>" + data
.subText
+ "</qt>");
228 d
->imageLabel
->setPixmap(data
.image
);
229 d
->windowToPreview
= data
.windowToPreview
;
230 d
->preview
->setWindowId( d
->windowToPreview
);
235 if (d
->currentWidget
) {
236 move(d
->currentWidget
->popupPosition(size()));
241 void ToolTip::themeUpdated()
243 const int topHeight
= d
->background
->marginSize(Plasma::TopMargin
);
244 const int leftWidth
= d
->background
->marginSize(Plasma::LeftMargin
);
245 const int rightWidth
= d
->background
->marginSize(Plasma::RightMargin
);
246 const int bottomHeight
= d
->background
->marginSize(Plasma::BottomMargin
);
247 setContentsMargins(leftWidth
, topHeight
, rightWidth
, bottomHeight
);
249 // Make the tooltip use Plasma's colorscheme
250 QPalette plasmaPalette
= QPalette();
251 plasmaPalette
.setColor(QPalette::Window
, Plasma::Theme::self()->backgroundColor());
252 plasmaPalette
.setColor(QPalette::WindowText
, Plasma::Theme::self()->textColor());
253 setAutoFillBackground(true);
254 setPalette(plasmaPalette
);
257 // A widget which reserves area for window preview and sets hints on the toplevel
258 // tooltip widget that tells KWin to render the preview in this area. This depends
259 // on KWin's TaskbarThumbnail compositing effect (which is here detected).
261 void WindowPreview::setWindowId(WId w
)
263 if (!previewsAvailable()) {
271 bool WindowPreview::previewsAvailable() const
273 if (!KWindowSystem::compositingActive()) {
277 // hackish way to find out if KWin has the effect enabled,
278 // TODO provide proper support
279 Display
* dpy
= QX11Info::display();
280 Atom atom
= XInternAtom(dpy
, "_KDE_WINDOW_PREVIEW", False
);
282 Atom
* list
= XListProperties(dpy
, DefaultRootWindow( dpy
), &cnt
);
284 bool ret
= ( qFind(list
, list
+ cnt
, atom
) != list
+ cnt
);
292 QSize
WindowPreview::sizeHint() const
297 if (!windowSize
.isValid()) {
300 QSize s
= windowSize
;
301 s
.scale(200, 150, Qt::KeepAspectRatio
);
305 void WindowPreview::readWindowSize() const
310 unsigned int w
, h
, b
, d
;
311 if (XGetGeometry(QX11Info::display(), id
, &r
, &x
, &y
, &w
, &h
, &b
, &d
)) {
312 windowSize
= QSize( w
, h
);
314 windowSize
= QSize();
317 windowSize
= QSize();
321 void WindowPreview::setInfo()
324 Display
*dpy
= QX11Info::display();
325 Atom atom
= XInternAtom(dpy
, "_KDE_WINDOW_PREVIEW", False
);
327 XDeleteProperty(dpy
, parentWidget()->winId(), atom
);
330 if (!windowSize
.isValid()) {
333 if (!windowSize
.isValid()) {
334 XDeleteProperty(dpy
, parentWidget()->winId(), atom
);
337 Q_ASSERT( parentWidget()->isWindow()); // parent must be toplevel
338 long data
[] = { 1, 5, id
, x(), y(), width(), height() };
339 XChangeProperty(dpy
, parentWidget()->winId(), atom
, atom
, 32, PropModeReplace
,
340 reinterpret_cast< unsigned char* >( data
), sizeof( data
) / sizeof( data
[ 0 ] ));
344 void ToolTip::resizeEvent(QResizeEvent
*e
)
346 QWidget::resizeEvent(e
);
347 d
->background
->resize(size());
349 if (KWindowSystem::compositingActive()) {
353 QBitmap
mask(width(), height());
354 QPainter
maskPainter(&mask
);
356 mask
.fill(Qt::white
);
358 maskPainter
.setBrush(Qt::black
);
359 maskPainter
.setPen(Qt::black
);
361 maskPainter
.drawPath(roundedRectangle(mask
.rect().adjusted(-1,-1,-1,-1), 10));
365 void ToolTip::paintEvent(QPaintEvent
*e
)
367 QPainter
painter(this);
368 painter
.setRenderHint(QPainter::Antialiasing
);
369 painter
.setClipRect(e
->rect());
370 painter
.setCompositionMode(QPainter::CompositionMode_Source
);
371 painter
.fillRect(rect(), Qt::transparent
);
373 d
->background
->paint(&painter
, rect());
375 //Stroke border if there is no compositing
376 if (!KWindowSystem::compositingActive()) {
377 painter
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
378 painter
.setPen(Plasma::Theme::self()->textColor());
379 painter
.drawPath(roundedRectangle(rect().adjusted(.5,.5,-.5,-.5), 10));
384 #include "tooltip_p.moc"