source: trunk/src/gui/kernel/qtooltip.cpp@ 284

Last change on this file since 284 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 17.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41#ifdef Q_WS_MAC
42# include <private/qcore_mac_p.h>
43#endif
44
45#include <qapplication.h>
46#include <qdesktopwidget.h>
47#include <qevent.h>
48#include <qhash.h>
49#include <qlabel.h>
50#include <qpointer.h>
51#include <qstyle.h>
52#include <qstyleoption.h>
53#include <qstylepainter.h>
54#include <qtimer.h>
55#include <qtooltip.h>
56#include <private/qeffects_p.h>
57#include <qtextdocument.h>
58#include <qdebug.h>
59#include <private/qstylesheetstyle_p.h>
60#ifndef QT_NO_TOOLTIP
61
62#ifdef Q_WS_MAC
63# include <private/qcore_mac_p.h>
64#include <private/qt_cocoa_helpers_mac_p.h>
65#endif
66
67QT_BEGIN_NAMESPACE
68
69/*!
70 \class QToolTip
71
72 \brief The QToolTip class provides tool tips (balloon help) for any
73 widget.
74
75 \ingroup helpsystem
76 \mainclass
77
78 The tip is a short piece of text reminding the user of the
79 widget's function. It is drawn immediately below the given
80 position in a distinctive black-on-yellow color combination. The
81 tip can be any \l{QTextEdit}{rich text} formatted string.
82
83 Rich text displayed in a tool tip is implicitly word-wrapped unless
84 specified differently with \c{<p style='white-space:pre'>}.
85
86 The simplest and most common way to set a widget's tool tip is by
87 calling its QWidget::setToolTip() function.
88
89 It is also possible to show different tool tips for different
90 regions of a widget, by using a QHelpEvent of type
91 QEvent::ToolTip. Intercept the help event in your widget's \l
92 {QWidget::}{event()} function and call QToolTip::showText() with
93 the text you want to display. The \l{widgets/tooltips}{Tooltips}
94 example illustrates this technique.
95
96 If you are calling QToolTip::hideText(), or QToolTip::showText()
97 with an empty string, as a result of a \l{QEvent::}{ToolTip}-event you
98 should also call \l{QEvent::}{ignore()} on the event, to signal
99 that you don't want to start any tooltip specific modes.
100
101 Note that, if you want to show tooltips in an item view, the
102 model/view architecture provides functionality to set an item's
103 tool tip; e.g., the QTableWidgetItem::setToolTip() function.
104 However, if you want to provide custom tool tips in an item view,
105 you must intercept the help event in the
106 QAbstractItemView::viewportEvent() function and handle it yourself.
107
108 The default tool tip color and font can be customized with
109 setPalette() and setFont(). When a tooltip is currently on
110 display, isVisible() returns true and text() the currently visible
111 text.
112
113 \note Tool tips use the inactive color group of QPalette, because tool
114 tips are not active windows.
115
116 \sa QWidget::toolTip, QAction::toolTip, {Tool Tips Example}
117*/
118
119class QTipLabel : public QLabel
120{
121 Q_OBJECT
122public:
123 QTipLabel(const QString &text, QWidget *w);
124 ~QTipLabel();
125 static QTipLabel *instance;
126
127 bool eventFilter(QObject *, QEvent *);
128
129 QBasicTimer hideTimer;
130 bool fadingOut;
131
132 void reuseTip(const QString &text);
133 void hideTip();
134 void hideTipImmediately();
135 void setTipRect(QWidget *w, const QRect &r);
136 void restartHideTimer();
137 bool tipChanged(const QPoint &pos, const QString &text, QObject *o);
138 void placeTip(const QPoint &pos, QWidget *w);
139
140 static int getTipScreen(const QPoint &pos, QWidget *w);
141protected:
142 void timerEvent(QTimerEvent *e);
143 void paintEvent(QPaintEvent *e);
144 void mouseMoveEvent(QMouseEvent *e);
145 void resizeEvent(QResizeEvent *e);
146
147#ifndef QT_NO_STYLE_STYLESHEET
148public slots:
149 /** \internal
150 Cleanup the _q_stylesheet_parent propery.
151 */
152 void styleSheetParentDestroyed() {
153 setProperty("_q_stylesheet_parent", QVariant());
154 styleSheetParent = 0;
155 }
156
157private:
158 QWidget *styleSheetParent;
159#endif
160
161private:
162 QWidget *widget;
163 QRect rect;
164};
165
166QTipLabel *QTipLabel::instance = 0;
167
168QTipLabel::QTipLabel(const QString &text, QWidget *w)
169#ifndef QT_NO_STYLE_STYLESHEET
170 : QLabel(w, Qt::ToolTip), styleSheetParent(0), widget(0)
171#else
172 : QLabel(w, Qt::ToolTip), widget(0)
173#endif
174{
175 delete instance;
176 instance = this;
177 setForegroundRole(QPalette::ToolTipText);
178 setBackgroundRole(QPalette::ToolTipBase);
179 setPalette(QToolTip::palette());
180 ensurePolished();
181 setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, this));
182 setFrameStyle(QFrame::NoFrame);
183 setAlignment(Qt::AlignLeft);
184 setIndent(1);
185 setWordWrap(Qt::mightBeRichText(text));
186 qApp->installEventFilter(this);
187 setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0);
188 setMouseTracking(true);
189 fadingOut = false;
190 reuseTip(text);
191}
192
193void QTipLabel::restartHideTimer()
194{
195 int time = 10000 + 40 * qMax(0, text().length()-100);
196 hideTimer.start(time, this);
197}
198
199void QTipLabel::reuseTip(const QString &text)
200{
201#ifndef QT_NO_STYLE_STYLESHEET
202 if (styleSheetParent) {
203 disconnect(styleSheetParent, SIGNAL(destroyed()),
204 QTipLabel::instance, SLOT(styleSheetParentDestroyed()));
205 styleSheetParent = 0;
206 }
207#endif
208
209 setText(text);
210 QFontMetrics fm(font());
211 QSize extra(1, 0);
212 // Make it look good with the default ToolTip font on Mac, which has a small descent.
213 if (fm.descent() == 2 && fm.ascent() >= 11)
214 ++extra.rheight();
215 resize(sizeHint() + extra);
216 restartHideTimer();
217}
218
219void QTipLabel::paintEvent(QPaintEvent *ev)
220{
221 QStylePainter p(this);
222 QStyleOptionFrame opt;
223 opt.init(this);
224 p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
225 p.end();
226
227 QLabel::paintEvent(ev);
228}
229
230void QTipLabel::resizeEvent(QResizeEvent *e)
231{
232 QStyleHintReturnMask frameMask;
233 QStyleOption option;
234 option.init(this);
235 if (style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
236 setMask(frameMask.region);
237
238 QLabel::resizeEvent(e);
239}
240
241void QTipLabel::mouseMoveEvent(QMouseEvent *e)
242{
243 if (rect.isNull())
244 return;
245 QPoint pos = e->globalPos();
246 if (widget)
247 pos = widget->mapFromGlobal(pos);
248 if (!rect.contains(pos))
249 hideTip();
250 QLabel::mouseMoveEvent(e);
251}
252
253QTipLabel::~QTipLabel()
254{
255 instance = 0;
256}
257
258void QTipLabel::hideTip()
259{
260 hideTimer.start(300, this);
261}
262
263void QTipLabel::hideTipImmediately()
264{
265 close(); // to trigger QEvent::Close which stops the animation
266 deleteLater();
267}
268
269void QTipLabel::setTipRect(QWidget *w, const QRect &r)
270{
271 if (!rect.isNull() && !w)
272 qWarning("QToolTip::setTipRect: Cannot pass null widget if rect is set");
273 else{
274 widget = w;
275 rect = r;
276 }
277}
278
279void QTipLabel::timerEvent(QTimerEvent *e)
280{
281 if (e->timerId() == hideTimer.timerId()){
282 hideTimer.stop();
283#if defined(Q_WS_MAC) && !defined(QT_NO_EFFECTS)
284 if (QApplication::isEffectEnabled(Qt::UI_FadeTooltip)){
285 // Fade out tip on mac (makes it invisible).
286 // The tip will not be deleted until a new tip is shown.
287
288 // DRSWAT - Cocoa
289 macWindowFade(qt_mac_window_for(this));
290 QTipLabel::instance->fadingOut = true; // will never be false again.
291 }
292 else
293 hideTipImmediately();
294#else
295 hideTipImmediately();
296#endif
297 }
298}
299
300bool QTipLabel::eventFilter(QObject *o, QEvent *e)
301{
302 switch (e->type()) {
303#ifdef Q_WS_MAC
304 case QEvent::KeyPress:
305 case QEvent::KeyRelease: {
306 int key = static_cast<QKeyEvent *>(e)->key();
307 Qt::KeyboardModifiers mody = static_cast<QKeyEvent *>(e)->modifiers();
308 if (!(mody & Qt::KeyboardModifierMask)
309 && key != Qt::Key_Shift && key != Qt::Key_Control
310 && key != Qt::Key_Alt && key != Qt::Key_Meta)
311 hideTip();
312 break;
313 }
314#endif
315 case QEvent::Leave:
316 hideTip();
317 break;
318 case QEvent::WindowActivate:
319 case QEvent::WindowDeactivate:
320 case QEvent::MouseButtonPress:
321 case QEvent::MouseButtonRelease:
322 case QEvent::MouseButtonDblClick:
323 case QEvent::FocusIn:
324 case QEvent::FocusOut:
325 case QEvent::Wheel:
326 hideTipImmediately();
327 break;
328
329 case QEvent::MouseMove:
330 if (o == widget && !rect.isNull() && !rect.contains(static_cast<QMouseEvent*>(e)->pos()))
331 hideTip();
332 default:
333 break;
334 }
335 return false;
336}
337
338int QTipLabel::getTipScreen(const QPoint &pos, QWidget *w)
339{
340 if (QApplication::desktop()->isVirtualDesktop())
341 return QApplication::desktop()->screenNumber(pos);
342 else
343 return QApplication::desktop()->screenNumber(w);
344}
345
346void QTipLabel::placeTip(const QPoint &pos, QWidget *w)
347{
348#ifndef QT_NO_STYLE_STYLESHEET
349 if (testAttribute(Qt::WA_StyleSheet) || (w && qobject_cast<QStyleSheetStyle *>(w->style()))) {
350 //the stylesheet need to know the real parent
351 QTipLabel::instance->setProperty("_q_stylesheet_parent", qVariantFromValue(w));
352 //we force the style to be the QStyleSheetStyle, and force to clear the cache as well.
353 QTipLabel::instance->setStyleSheet(QLatin1String("/* */"));
354
355 // Set up for cleaning up this later...
356 QTipLabel::instance->styleSheetParent = w;
357 if (w) {
358 connect(w, SIGNAL(destroyed()),
359 QTipLabel::instance, SLOT(styleSheetParentDestroyed()));
360 }
361 }
362#endif //QT_NO_STYLE_STYLESHEET
363
364
365#ifdef Q_WS_MAC
366 QRect screen = QApplication::desktop()->availableGeometry(getTipScreen(pos, w));
367#else
368 QRect screen = QApplication::desktop()->screenGeometry(getTipScreen(pos, w));
369#endif
370