Ok, the long awaited config wizard is here (at least in a very basic state). There...
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / gui / qxttooltip.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
4 **
5 ** This file is part of the QxtGui module of the Qt eXTension library
6 **
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
9 ** IBM.
10 **
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
15 **
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
20 **
21 ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
22 **
23 ****************************************************************************/
24 #include "qxttooltip.h"
25 #include "qxttooltip_p.h"
26 #include <QStyleOptionFrame>
27 #include <QDesktopWidget>
28 #include <QStylePainter>
29 #include <QApplication>
30 #include <QVBoxLayout>
31 #include <QMouseEvent>
32 #include <QKeyEvent>
33 #include <QToolTip>
34 #include <QPalette>
35 #include <QTimer>
36 #include <QFrame>
37 #include <QStyle>
38 #include <QHash>
39
40 static const Qt::WindowFlags FLAGS = Qt::ToolTip;
41
42 QxtToolTipPrivate* QxtToolTipPrivate::self = 0;
43
44 QxtToolTipPrivate* QxtToolTipPrivate::instance()
45 {
46     if (!self)
47         self = new QxtToolTipPrivate();
48     return self;
49 }
50
51 QxtToolTipPrivate::QxtToolTipPrivate() : QWidget(qApp->desktop(), FLAGS)
52 {
53     setWindowFlags(FLAGS);
54     vbox = new QVBoxLayout(this);
55     setPalette(QToolTip::palette());
56     setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0);
57     layout()->setMargin(style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, this));
58     qApp->installEventFilter(this);
59 }
60
61 QxtToolTipPrivate::~QxtToolTipPrivate()
62 {
63     qApp->removeEventFilter(this); // not really necessary but rather for completeness :)
64     self = 0;
65 }
66
67 void QxtToolTipPrivate::show(const QPoint& pos, QWidget* tooltip, QWidget* parent, const QRect& rect)
68 {
69     Q_ASSERT(tooltip && parent);
70     if (!isVisible())
71     {
72         int scr = 0;
73         if (QApplication::desktop()->isVirtualDesktop())
74             scr = QApplication::desktop()->screenNumber(pos);
75         else
76             scr = QApplication::desktop()->screenNumber(this);
77         setParent(QApplication::desktop()->screen(scr));
78         setWindowFlags(FLAGS);
79         setToolTip(tooltip);
80         currentParent = parent;
81         currentRect = rect;
82         move(calculatePos(scr, pos));
83         QWidget::show();
84     }
85 }
86
87 void QxtToolTipPrivate::setToolTip(QWidget* tooltip)
88 {
89     for (int i = 0; i < vbox->count(); ++i)
90     {
91         QLayoutItem* item = layout()->takeAt(i);
92         if (item->widget())
93             item->widget()->hide();
94     }
95     vbox->addWidget(tooltip);
96     tooltip->show();
97 }
98
99 void QxtToolTipPrivate::enterEvent(QEvent* event)
100 {
101     Q_UNUSED(event);
102     hideLater();
103 }
104
105 void QxtToolTipPrivate::paintEvent(QPaintEvent* event)
106 {
107     Q_UNUSED(event);
108     QStylePainter painter(this);
109     QStyleOptionFrame opt;
110     opt.initFrom(this);
111     painter.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
112 }
113
114 bool QxtToolTipPrivate::eventFilter(QObject* object, QEvent* event)
115 {
116     switch (event->type())
117     {
118     case QEvent::KeyPress:
119     case QEvent::KeyRelease:
120     {
121         // accept only modifiers
122         const QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
123         const int key = keyEvent->key();
124         const Qt::KeyboardModifiers mods = keyEvent->modifiers();
125         if ((mods & Qt::KeyboardModifierMask) ||
126                 (key == Qt::Key_Shift || key == Qt::Key_Control ||
127                  key == Qt::Key_Alt || key == Qt::Key_Meta))
128             break;
129     }
130     case QEvent::Leave:
131     case QEvent::WindowActivate:
132     case QEvent::WindowDeactivate:
133     case QEvent::MouseButtonPress:
134     case QEvent::MouseButtonRelease:
135     case QEvent::MouseButtonDblClick:
136     case QEvent::FocusIn:
137     case QEvent::FocusOut:
138     case QEvent::Wheel:
139         hideLater();
140         break;
141
142     case QEvent::MouseMove:
143     {
144         const QPoint pos = static_cast<QMouseEvent*>(event)->pos();
145         if (!currentRect.isNull() && !currentRect.contains(pos))
146         {
147             hideLater();
148         }
149         break;
150     }
151
152     case QEvent::ToolTip:
153     {
154         // eat appropriate tooltip events
155         QWidget* widget = static_cast<QWidget*>(object);
156         if (tooltips.contains(widget))
157         {
158             QHelpEvent* helpEvent = static_cast<QHelpEvent*>(event);
159             const QRect area = tooltips.value(widget).second;
160             if (area.isNull() || area.contains(helpEvent->pos()))
161             {
162                 show(helpEvent->globalPos(), tooltips.value(widget).first, widget, area);
163                 return true;
164             }
165         }
166     }
167
168     default:
169         break;
170     }
171     return false;
172 }
173
174 void QxtToolTipPrivate::hideLater()
175 {
176     currentRect = QRect();
177     if (isVisible())
178         QTimer::singleShot(0, this, SLOT(hide()));
179 }
180
181 QPoint QxtToolTipPrivate::calculatePos(int scr, const QPoint& eventPos) const
182 {
183 #ifdef Q_WS_MAC
184     QRect screen = QApplication::desktop()->availableGeometry(scr);
185 #else
186     QRect screen = QApplication::desktop()->screenGeometry(scr);
187 #endif
188
189     QPoint p = eventPos;
190     p += QPoint(2,
191 #ifdef Q_WS_WIN
192                 24
193 #else
194                 16
195 #endif
196                );
197     QSize s = sizeHint();
198     if (p.x() + s.width() > screen.x() + screen.width())
199         p.rx() -= 4 + s.width();
200     if (p.y() + s.height() > screen.y() + screen.height())
201         p.ry() -= 24 + s.height();
202     if (p.y() < screen.y())
203         p.setY(screen.y());
204     if (p.x() + s.width() > screen.x() + screen.width())
205         p.setX(screen.x() + screen.width() - s.width());
206     if (p.x() < screen.x())
207         p.setX(screen.x());
208     if (p.y() + s.height() > screen.y() + screen.height())
209         p.setY(screen.y() + screen.height() - s.height());
210     return p;
211 }
212
213 /*!
214     \class QxtToolTip QxtToolTip
215     \ingroup QxtGui
216     \brief Show any arbitrary widget as a tooltip.
217
218     QxtToolTip provides means for showing any arbitrary widget as a tooltip.
219
220     \note The rich text support of QToolTip already makes it possible to
221     show heavily customized tooltips with lists, tables, embedded images
222     and such. However, for example dynamically created images like
223     thumbnails cause problems. Basically the only way is to dump the
224     thumbnail to a temporary file to be able to embed it into HTML. This
225     is where QxtToolTip steps in. A generated thumbnail may simply be set
226     on a QLabel which is then shown as a tooltip. Yet another use case
227     is a tooltip with dynamically changing content.
228
229     \image html qxttooltip.png "QxtToolTip in action."
230
231     \warning Added tooltip widgets remain in the memory for the lifetime
232     of the application or until they are removed/deleted. Do NOT flood your
233     application up with lots of complex tooltip widgets or it will end up
234     being a resource hog. QToolTip is sufficient for most of the cases!
235  */
236
237 /*!
238     Shows the \a tooltip at \a pos for \a parent at \a rect.
239
240     \sa hide()
241 */
242 void QxtToolTip::show(const QPoint& pos, QWidget* tooltip, QWidget* parent, const QRect& rect)
243 {
244     QxtToolTipPrivate::instance()->show(pos, tooltip, parent, rect);
245 }
246
247 /*!
248     Hides the tooltip.
249
250     \sa show()
251 */
252 void QxtToolTip::hide()
253 {
254     QxtToolTipPrivate::instance()->hide();
255 }
256
257 /*!
258     Returns the tooltip for \a parent.
259
260     \sa setToolTip()
261 */
262 QWidget* QxtToolTip::toolTip(QWidget* parent)
263 {
264     Q_ASSERT(parent);
265     QWidget* tooltip = 0;
266     if (!QxtToolTipPrivate::instance()->tooltips.contains(parent))
267         qWarning("QxtToolTip::toolTip: Unknown parent");
268     else
269         tooltip = QxtToolTipPrivate::instance()->tooltips.value(parent).first;
270     return tooltip;
271 }
272
273 /*!
274     Sets the \a tooltip to be shown for \a parent.
275     An optional \a rect may also be passed.
276
277     \sa toolTip()
278 */
279 void QxtToolTip::setToolTip(QWidget* parent, QWidget* tooltip, const QRect& rect)
280 {
281     Q_ASSERT(parent);
282     if (tooltip)
283     {
284         // set
285         tooltip->hide();
286         QxtToolTipPrivate::instance()->tooltips[parent] = qMakePair(QPointer<QWidget>(tooltip), rect);
287     }
288     else
289     {
290         // remove
291         if (!QxtToolTipPrivate::instance()->tooltips.contains(parent))
292             qWarning("QxtToolTip::setToolTip: Unknown parent");
293         else
294             QxtToolTipPrivate::instance()->tooltips.remove(parent);
295     }
296 }
297
298 /*!
299     Returns the rect on which tooltip is shown for \a parent.
300
301     \sa setToolTipRect()
302 */
303 QRect QxtToolTip::toolTipRect(QWidget* parent)
304 {
305     Q_ASSERT(parent);
306     QRect rect;
307     if (!QxtToolTipPrivate::instance()->tooltips.contains(parent))
308         qWarning("QxtToolTip::toolTipRect: Unknown parent");
309     else
310         rect = QxtToolTipPrivate::instance()->tooltips.value(parent).second;
311     return rect;
312 }
313
314 /*!
315     Sets the \a rect on which tooltip is shown for \a parent.
316
317     \sa toolTipRect()
318 */
319 void QxtToolTip::setToolTipRect(QWidget* parent, const QRect& rect)
320 {
321     Q_ASSERT(parent);
322     if (!QxtToolTipPrivate::instance()->tooltips.contains(parent))
323         qWarning("QxtToolTip::setToolTipRect: Unknown parent");
324     else
325         QxtToolTipPrivate::instance()->tooltips[parent].second = rect;
326 }
327
328 /*!
329     Returns the margin of the tooltip.
330
331     \sa setMargin()
332 */
333 int QxtToolTip::margin()
334 {
335     return QxtToolTipPrivate::instance()->layout()->margin();
336 }
337
338 /*!
339     Sets the margin of the tooltip.
340
341     The default value is \b QStyle::PM_ToolTipLabelFrameWidth.
342
343     \sa margin()
344 */
345 void QxtToolTip::setMargin(int margin)
346 {
347     QxtToolTipPrivate::instance()->layout()->setMargin(margin);
348 }
349
350 /*!
351     Returns the opacity level of the tooltip.
352
353     \sa QWidget::windowOpacity()
354 */
355 qreal QxtToolTip::opacity()
356 {
357     return QxtToolTipPrivate::instance()->windowOpacity();
358 }
359
360 /*!
361     Sets the opacity level of the tooltip.
362
363     The default value is \b QStyle::SH_ToolTipLabel_Opacity.
364
365     \sa QWidget::setWindowOpacity()
366 */
367 void QxtToolTip::setOpacity(qreal level)
368 {
369     QxtToolTipPrivate::instance()->setWindowOpacity(level);
370 }