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

Last change on this file since 336 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
371 QPoint p = pos;
372 p += QPoint(2,
373#ifdef Q_WS_WIN
374 21
375#else
376 16
377#endif
378 );
379 if (p.x() + this->width() > screen.x() + screen.width())
380 p.rx() -= 4 + this->width();
381 if (p.y() + this->height() > screen.y() + screen.height())
382 p.ry() -= 24 + this->height();
383 if (p.y() < screen.y())
384 p.setY(screen.y());
385 if (p.x() + this->width() > screen.x() + screen.width())
386 p.setX(screen.x() + screen.width() - this->width());
387 if (p.x() < screen.x())
388 p.setX(screen.x());
389 if (p.y() + this->height() > screen.y() + screen.height())
390 p.setY(screen.y() + screen.height() - this->height());
391 this->move(p);
392}
393
394bool QTipLabel::tipChanged(const QPoint &pos, const QString &text, QObject *o)
395{
396 if (QTipLabel::instance->text() != text)
397 return true;
398
399 if (o != widget)
400 return true;
401
402 if (!rect.isNull())
403 return !rect.contains(pos);
404 else
405 return false;
406}
407
408/*!
409 Shows \a text as a tool tip, with the global position \a pos as
410 the point of interest. The tool tip will be shown with a platform
411 specific offset from this point of interest.
412
413 If you specify a non-empty rect the tip will be hidden as soon
414 as you move your cursor out of this area.
415
416 The \a rect is in the coordinates of the widget you specify with
417 \a w. If the \a rect is not empty you must specify a widget.
418 Otherwise this argument can be 0 but it is used to determine the
419 appropriate screen on multi-head systems.
420
421 If \a text is empty the tool tip is hidden. If the text is the
422 same as the currently shown tooltip, the tip will \e not move.
423 You can force moving by first hiding the tip with an empty text,
424 and then showing the new tip at the new position.
425*/
426
427void QToolTip::showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect)
428{
429 if (QTipLabel::instance){ // a tip does already exist
430 if (text.isEmpty()){ // empty text means hide current tip
431 QTipLabel::instance->hideTip();
432 return;
433 }
434 else if (!QTipLabel::instance->fadingOut){
435 // If the tip has changed, reuse the one
436 // that is showing (removes flickering)
437 QPoint localPos = pos;
438 if (w)
439 localPos = w->mapFromGlobal(pos);
440 if (QTipLabel::instance->tipChanged(localPos, text, w)){
441 QTipLabel::instance->reuseTip(text);
442 QTipLabel::instance->setTipRect(w, rect);
443 QTipLabel::instance->placeTip(pos, w);
444 }
445 return;
446 }
447 }
448
449 if (!text.isEmpty()){ // no tip can be reused, create new tip:
450#ifndef Q_WS_WIN
451 new QTipLabel(text, w); // sets QTipLabel::instance to itself
452#else
453 // On windows, we can't use the widget as parent otherwise the window will be
454 // raised when the tooltip will be shown
455 new QTipLabel(text, QApplication::desktop()->screen(QTipLabel::getTipScreen(pos, w)));
456#endif
457 QTipLabel::instance->setTipRect(w, rect);
458 QTipLabel::instance->placeTip(pos, w);
459 QTipLabel::instance->setObjectName(QLatin1String("qtooltip_label"));
460
461
462#if !defined(QT_NO_EFFECTS) && !defined(Q_WS_MAC)
463 if (QApplication::isEffectEnabled(Qt::UI_FadeTooltip))
464 qFadeEffect(QTipLabel::instance);
465 else if (QApplication::isEffectEnabled(Qt::UI_AnimateTooltip))
466 qScrollEffect(QTipLabel::instance);
467 else
468 QTipLabel::instance->show();
469#else
470 QTipLabel::instance->show();
471#endif
472 }
473}
474
475/*!
476 \overload
477
478 This is analogous to calling QToolTip::showText(\a pos, \a text, \a w, QRect())
479*/
480
481void QToolTip::showText(const QPoint &pos, const QString &text, QWidget *w)
482{
483 QToolTip::showText(pos, text, w, QRect());
484}
485
486
487/*!
488 \fn void QToolTip::hideText()
489 \since 4.2
490
491 Hides the tool tip. This is the same as calling showText() with an
492 empty string.
493
494 \sa showText()
495*/
496
497
498/*!
499 \since 4.4
500
501 Returns true if this tooltip is currently shown.
502
503 \sa showText()
504 */
505bool QToolTip::isVisible()
506{
507 return (QTipLabel::instance != 0 && QTipLabel::instance->isVisible());
508}
509
510/*!
511 \since 4.4
512
513 Returns the tooltip text, if a tooltip is visible, or an
514 empty string if a tooltip is not visible.
515 */
516QString QToolTip::text()
517{
518 if (QTipLabel::instance)
519 return QTipLabel::instance->text();
520 return QString();
521}
522
523
524Q_GLOBAL_STATIC(QPalette, tooltip_palette)
525
526/*!
527 Returns the palette used to render tooltips.
528
529 \note Tool tips use the inactive color group of QPalette, because tool
530 tips are not active windows.
531*/
532QPalette QToolTip::palette()
533{
534 return *tooltip_palette();
535}
536
537/*!
538 \since 4.2
539
540 Returns the font used to render tooltips.
541*/
542QFont QToolTip::font()
543{
544 return QApplication::font("QTipLabel");
545}
546
547/*!
548 \since 4.2
549
550 Sets the \a palette used to render tooltips.
551
552 \note Tool tips use the inactive color group of QPalette, because tool
553 tips are not active windows.
554*/
555void QToolTip::setPalette(const QPalette &palette)
556{
557 *tooltip_palette() = palette;
558 if (QTipLabel::instance)
559 QTipLabel::instance->setPalette(palette);
560}
561
562/*!
563 \since 4.2
564
565 Sets the \a font used to render tooltips.
566*/
567void QToolTip::setFont(const QFont &font)
568{
569 QApplication::setFont(font, "QTipLabel");
570}
571
572
573/*!
574 \fn void QToolTip::add(QWidget *widget, const QString &text)
575
576 Use QWidget::setToolTip() instead.
577
578 \oldcode
579 tip->add(widget, text);
580 \newcode
581 widget->setToolTip(text);
582 \endcode
583*/
584
585/*!
586 \fn void QToolTip::add(QWidget *widget, const QRect &rect, const QString &text)
587
588 Intercept the QEvent::ToolTip events in your widget's
589 QWidget::event() function and call QToolTip::showText() with the
590 text you want to display. The \l{widgets/tooltips}{Tooltips}
591 example illustrates this technique.
592*/
593
594/*!
595 \fn void QToolTip::remove(QWidget *widget)
596
597 Use QWidget::setToolTip() instead.
598
599 \oldcode
600 tip->remove(widget);
601 \newcode
602 widget->setToolTip("");
603 \endcode
604*/
605
606QT_END_NAMESPACE
607
608#include "qtooltip.moc"
609#endif // QT_NO_TOOLTIP
Note: See TracBrowser for help on using the repository browser.