source: trunk/src/gui/kernel/qwhatsthis.cpp@ 477

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

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

File size: 22.6 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
42#include "qwhatsthis.h"
43#ifndef QT_NO_WHATSTHIS
44#include "qpointer.h"
45#include "qapplication.h"
46#include "qdesktopwidget.h"
47#include "qevent.h"
48#include "qpixmap.h"
49#include "qpainter.h"
50#include "qtimer.h"
51#include "qhash.h"
52#include "qaction.h"
53#include "qcursor.h"
54#include "qbitmap.h"
55#include "qtextdocument.h"
56#include "../text/qtextdocumentlayout_p.h"
57#include "qtoolbutton.h"
58#include "qdebug.h"
59#ifndef QT_NO_ACCESSIBILITY
60#include "qaccessible.h"
61#endif
62#if defined(Q_WS_WIN)
63#include "qt_windows.h"
64#ifndef SPI_GETDROPSHADOW
65#define SPI_GETDROPSHADOW 0x1024
66#endif
67#endif
68#if defined(Q_WS_X11)
69#include "qx11info_x11.h"
70#include <qwidget.h>
71#endif
72
73QT_BEGIN_NAMESPACE
74
75/*!
76 \class QWhatsThis
77 \brief The QWhatsThis class provides a simple description of any
78 widget, i.e. answering the question "What's This?".
79
80 \ingroup helpsystem
81 \mainclass
82
83 "What's This?" help is part of an application's online help
84 system, and provides users with information about the
85 functionality and usage of a particular widget. "What's This?"
86 help texts are typically longer and more detailed than \link
87 QToolTip tooltips\endlink, but generally provide less information
88 than that supplied by separate help windows.
89
90 QWhatsThis provides a single window with an explanatory text that
91 pops up when the user asks "What's This?". The default way for
92 users to ask the question is to move the focus to the relevant
93 widget and press Shift+F1. The help text appears immediately; it
94 goes away as soon as the user does something else.
95 (Note that if there is a shortcut for Shift+F1, this mechanism
96 will not work.) Some dialogs provide a "?" button that users can
97 click to enter "What's This?" mode; they then click the relevant
98 widget to pop up the "What's This?" window. It is also possible to
99 provide a a menu option or toolbar button to switch into "What's
100 This?" mode.
101
102 To add "What's This?" text to a widget or an action, you simply
103 call QWidget::setWhatsThis() or QAction::setWhatsThis().
104
105 The text can be either rich text or plain text. If you specify a
106 rich text formatted string, it will be rendered using the default
107 stylesheet, making it possible to embed images in the displayed
108 text. To be as fast as possible, the default stylesheet uses a
109 simple method to determine whether the text can be rendered as
110 plain text. See Qt::mightBeRichText() for details.
111
112 \snippet doc/src/snippets/whatsthis/whatsthis.cpp 0
113
114 An alternative way to enter "What's This?" mode is to call
115 createAction(), and add the returned QAction to either a menu or
116 a tool bar. By invoking this context help action (in the picture
117 below, the button with the arrow and question mark icon) the user
118 switches into "What's This?" mode. If they now click on a widget
119 the appropriate help text is shown. The mode is left when help is
120 given or when the user presses Esc.
121
122 \img whatsthis.png
123
124 You can enter "What's This?" mode programmatically with
125 enterWhatsThisMode(), check the mode with inWhatsThisMode(), and
126 return to normal mode with leaveWhatsThisMode().
127
128 If you want to control the "What's This?" behavior of a widget
129 manually see Qt::WA_CustomWhatsThis.
130
131 It is also possible to show different help texts for different
132 regions of a widget, by using a QHelpEvent of type
133 QEvent::WhatsThis. Intercept the help event in your widget's
134 QWidget::event() function and call QWhatsThis::showText() with the
135 text you want to display for the position specified in
136 QHelpEvent::pos(). If the text is rich text and the user clicks
137 on a link, the widget also receives a QWhatsThisClickedEvent with
138 the link's reference as QWhatsThisClickedEvent::href(). If a
139 QWhatsThisClickedEvent is handled (i.e. QWidget::event() returns
140 true), the help window remains visible. Call
141 QWhatsThis::hideText() to hide it explicitly.
142
143 \sa QToolTip
144*/
145
146extern void qDeleteInEventHandler(QObject *o);
147
148class QWhatsThat : public QWidget
149{
150 Q_OBJECT
151
152public:
153 QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor);
154 ~QWhatsThat() ;
155
156 static QWhatsThat *instance;
157
158protected:
159 void showEvent(QShowEvent *e);
160 void mousePressEvent(QMouseEvent*);
161 void mouseReleaseEvent(QMouseEvent*);
162 void mouseMoveEvent(QMouseEvent*);
163 void keyPressEvent(QKeyEvent*);
164 void paintEvent(QPaintEvent*);
165
166private:
167 QPointer<QWidget>widget;
168 bool pressed;
169 QString text;
170 QTextDocument* doc;
171 QString anchor;
172 QPixmap background;
173};
174
175QWhatsThat *QWhatsThat::instance = 0;
176
177// shadowWidth not const, for XP drop-shadow-fu turns it to 0
178static int shadowWidth = 6; // also used as '5' and '6' and even '8' below
179static const int vMargin = 8;
180static const int hMargin = 12;
181
182QWhatsThat::QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor)
183 : QWidget(parent, Qt::Popup),
184 widget(showTextFor), pressed(false), text(txt)
185{
186 delete instance;
187 instance = this;
188 setAttribute(Qt::WA_DeleteOnClose, true);
189 setAttribute(Qt::WA_NoSystemBackground, true);
190 if (parent)
191 setPalette(parent->palette());
192 setMouseTracking(true);
193 setFocusPolicy(Qt::StrongFocus);
194#ifndef QT_NO_CURSOR
195 setCursor(Qt::ArrowCursor);
196#endif
197
198 QRect r;
199 doc = 0;
200 if (Qt::mightBeRichText(text)) {
201 doc = new QTextDocument();
202 doc->setUndoRedoEnabled(false);
203 doc->setDefaultFont(QApplication::font(this));
204#ifdef QT_NO_TEXTHTMLPARSER
205 doc->setPlainText(text);
206#else
207 doc->setHtml(text);
208#endif
209 doc->setUndoRedoEnabled(false);
210 doc->adjustSize();
211 r.setTop(0);
212 r.setLeft(0);
213 r.setSize(doc->size().toSize());
214 }
215 else
216 {
217 int sw = QApplication::desktop()->width() / 3;
218 if (sw < 200)
219 sw = 200;
220 else if (sw > 300)
221 sw = 300;
222
223 r = fontMetrics().boundingRect(0, 0, sw, 1000,
224 Qt::AlignLeft + Qt::AlignTop
225 + Qt::TextWordWrap + Qt::TextExpandTabs,
226 text);
227 }
228#if defined(Q_WS_WIN)
229 if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
230 BOOL shadow;
231 SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
232 shadowWidth = shadow ? 0 : 6;
233 }
234#endif
235 resize(r.width() + 2*hMargin + shadowWidth, r.height() + 2*vMargin + shadowWidth);
236}
237
238QWhatsThat::~QWhatsThat()
239{
240 instance = 0;
241 if (doc)
242 delete doc;
243}
244
245void QWhatsThat::showEvent(QShowEvent *)
246{
247 background = QPixmap::grabWindow(QApplication::desktop()->internalWinId(),
248 x(), y(), width(), height());
249}
250
251void QWhatsThat::mousePressEvent(QMouseEvent* e)
252{
253 pressed = true;
254 if (e->button() == Qt::LeftButton && rect().contains(e->pos())) {
255 if (doc)
256 anchor = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
257 return;
258 }
259 close();
260}
261
262void QWhatsThat::mouseReleaseEvent(QMouseEvent* e)
263{
264 if (!pressed)
265 return;
266 if (widget && e->button() == Qt::LeftButton && doc && rect().contains(e->pos())) {
267 QString a = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
268 QString href;
269 if (anchor == a)
270 href = a;
271 anchor.clear();
272 if (!href.isEmpty()) {
273 QWhatsThisClickedEvent e(href);
274 if (QApplication::sendEvent(widget, &e))
275 return;
276 }
277 }
278 close();
279}
280
281void QWhatsThat::mouseMoveEvent(QMouseEvent* e)
282{
283#ifdef QT_NO_CURSOR
284 Q_UNUSED(e);
285#else
286 if (!doc)
287 return;
288 QString a = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
289 if (!a.isEmpty())
290 setCursor(Qt::PointingHandCursor);
291 else
292 setCursor(Qt::ArrowCursor);
293#endif
294}
295
296void QWhatsThat::keyPressEvent(QKeyEvent*)
297{
298 close();
299}
300
301void QWhatsThat::paintEvent(QPaintEvent*)
302{
303 bool drawShadow = true;
304#if defined(Q_WS_WIN)
305 if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
306 BOOL shadow;
307 SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
308 drawShadow = !shadow;
309 }
310#elif defined(Q_WS_MAC) || defined(Q_WS_QWS)
311 drawShadow = false; // never draw it on OS X or QWS, as we get it for free
312#endif
313
314 QRect r = rect();
315 r.adjust(0, 0, -1, -1);
316 if (drawShadow)
317 r.adjust(0, 0, -shadowWidth, -shadowWidth);
318 QPainter p(this);
319 p.drawPixmap(0, 0, background);
320 p.setPen(QPen(palette().toolTipText(), 0));
321 p.setBrush(palette().toolTipBase());
322 p.drawRect(r);
323 int w = r.width();
324 int h = r.height();
325 p.setPen(palette().brush(QPalette::Dark).color());