1 | /****************************************************************************
|
---|
2 | ** $Id: qwhatsthis.cpp 2 2005-11-16 15:49:26Z dmik $
|
---|
3 | **
|
---|
4 | ** Implementation of QWhatsThis class
|
---|
5 | **
|
---|
6 | ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
|
---|
7 | **
|
---|
8 | ** This file is part of the widgets module of the Qt GUI Toolkit.
|
---|
9 | **
|
---|
10 | ** This file may be distributed under the terms of the Q Public License
|
---|
11 | ** as defined by Trolltech AS of Norway and appearing in the file
|
---|
12 | ** LICENSE.QPL included in the packaging of this file.
|
---|
13 | **
|
---|
14 | ** This file may be distributed and/or modified under the terms of the
|
---|
15 | ** GNU General Public License version 2 as published by the Free Software
|
---|
16 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
17 | ** packaging of this file.
|
---|
18 | **
|
---|
19 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
20 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
21 | ** Agreement provided with the Software.
|
---|
22 | **
|
---|
23 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
24 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
25 | **
|
---|
26 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
27 | ** information about Qt Commercial License Agreements.
|
---|
28 | ** See http://www.trolltech.com/qpl/ for QPL licensing information.
|
---|
29 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
30 | **
|
---|
31 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
32 | ** not clear to you.
|
---|
33 | **
|
---|
34 | **********************************************************************/
|
---|
35 |
|
---|
36 | #include "qwhatsthis.h"
|
---|
37 | #ifndef QT_NO_WHATSTHIS
|
---|
38 | #include "qapplication.h"
|
---|
39 | #include "qpaintdevicemetrics.h"
|
---|
40 | #include "qpixmap.h"
|
---|
41 | #include "qpainter.h"
|
---|
42 | #include "qtimer.h"
|
---|
43 | #include "qptrdict.h"
|
---|
44 | #include "qtoolbutton.h"
|
---|
45 | #include "qshared.h"
|
---|
46 | #include "qcursor.h"
|
---|
47 | #include "qbitmap.h"
|
---|
48 | #include "qtooltip.h"
|
---|
49 | #include "qsimplerichtext.h"
|
---|
50 | #include "qstylesheet.h"
|
---|
51 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
52 | #include "qaccessible.h"
|
---|
53 | #endif
|
---|
54 | #if defined(Q_WS_WIN)
|
---|
55 | #include "qt_windows.h"
|
---|
56 | #ifndef SPI_GETDROPSHADOW
|
---|
57 | #define SPI_GETDROPSHADOW 0x1024
|
---|
58 | #endif
|
---|
59 | #endif
|
---|
60 |
|
---|
61 | /*!
|
---|
62 | \class QWhatsThis qwhatsthis.h
|
---|
63 | \brief The QWhatsThis class provides a simple description of any
|
---|
64 | widget, i.e. answering the question "What's this?".
|
---|
65 |
|
---|
66 | \ingroup helpsystem
|
---|
67 | \mainclass
|
---|
68 |
|
---|
69 | "What's this?" help is part of an application's online help system
|
---|
70 | that provides users with information about functionality, usage,
|
---|
71 | background etc., in various levels of detail from short tool tips
|
---|
72 | to full text browsing help windows.
|
---|
73 |
|
---|
74 | QWhatsThis provides a single window with an explanatory text that
|
---|
75 | pops up when the user asks "What's this?". The default way to do
|
---|
76 | this is to focus the relevant widget and press Shift+F1. The help
|
---|
77 | text appears immediately; it goes away as soon as the user does
|
---|
78 | something else.
|
---|
79 |
|
---|
80 | (Note that if there is an accelerator for Shift+F1, this mechanism
|
---|
81 | will not work.)
|
---|
82 |
|
---|
83 | To add "What's this?" text to a widget you simply call
|
---|
84 | QWhatsThis::add() for the widget. For example, to assign text to a
|
---|
85 | menu item, call QMenuData::setWhatsThis(); for a global
|
---|
86 | accelerator key, call QAccel::setWhatsThis() and If you're using
|
---|
87 | actions, use QAction::setWhatsThis().
|
---|
88 |
|
---|
89 | The text can be either rich text or plain text. If you specify a
|
---|
90 | rich text formatted string, it will be rendered using the default
|
---|
91 | stylesheet. This makes it possible to embed images. See
|
---|
92 | QStyleSheet::defaultSheet() for details.
|
---|
93 |
|
---|
94 | \quotefile action/application.cpp
|
---|
95 | \skipto fileOpenText
|
---|
96 | \printuntil setWhatsThis
|
---|
97 |
|
---|
98 | An alternative way to enter "What's this?" mode is to use the
|
---|
99 | ready-made tool bar tool button from
|
---|
100 | QWhatsThis::whatsThisButton(). By invoking this context help
|
---|
101 | button (in the picture below the first one from the right) the
|
---|
102 | user switches into "What's this?" mode. If they now click on a
|
---|
103 | widget the appropriate help text is shown. The mode is left when
|
---|
104 | help is given or when the user presses Esc.
|
---|
105 |
|
---|
106 | \img whatsthis.png
|
---|
107 |
|
---|
108 | If you are using QMainWindow you can also use the
|
---|
109 | QMainWindow::whatsThis() slot to invoke the mode from a menu item.
|
---|
110 |
|
---|
111 | For more control you can create a dedicated QWhatsThis object for
|
---|
112 | a special widget. By subclassing and reimplementing
|
---|
113 | QWhatsThis::text() it is possible to have different help texts,
|
---|
114 | depending on the position of the mouse click. By reimplementing
|
---|
115 | QWhatsThis::clicked() it is possible to have hyperlinks inside the
|
---|
116 | help texts.
|
---|
117 |
|
---|
118 | If you wish to control the "What's this?" behavior of a widget
|
---|
119 | manually see QWidget::customWhatsThis().
|
---|
120 |
|
---|
121 | The What's This object can be removed using QWhatsThis::remove(),
|
---|
122 | although this is rarely necessary because it is automatically
|
---|
123 | removed when the widget is destroyed.
|
---|
124 |
|
---|
125 | \sa QToolTip
|
---|
126 | */
|
---|
127 |
|
---|
128 | // a special button
|
---|
129 | class QWhatsThisButton: public QToolButton
|
---|
130 | {
|
---|
131 | Q_OBJECT
|
---|
132 |
|
---|
133 | public:
|
---|
134 | QWhatsThisButton( QWidget * parent, const char * name );
|
---|
135 | ~QWhatsThisButton();
|
---|
136 |
|
---|
137 | public slots:
|
---|
138 | void mouseReleased();
|
---|
139 |
|
---|
140 | };
|
---|
141 |
|
---|
142 |
|
---|
143 | class QWhatsThat : public QWidget
|
---|
144 | {
|
---|
145 | Q_OBJECT
|
---|
146 | public:
|
---|
147 | QWhatsThat( QWidget* w, const QString& txt, QWidget* parent, const char* name );
|
---|
148 | ~QWhatsThat() ;
|
---|
149 |
|
---|
150 | public slots:
|
---|
151 | void hide();
|
---|
152 | inline void widgetDestroyed() { widget = 0; }
|
---|
153 |
|
---|
154 | protected:
|
---|
155 | void mousePressEvent( QMouseEvent* );
|
---|
156 | void mouseReleaseEvent( QMouseEvent* );
|
---|
157 | void mouseMoveEvent( QMouseEvent* );
|
---|
158 | void keyPressEvent( QKeyEvent* );
|
---|
159 | void paintEvent( QPaintEvent* );
|
---|
160 |
|
---|
161 | private:
|
---|
162 | QString text;
|
---|
163 | #ifndef QT_NO_RICHTEXT
|
---|
164 | QSimpleRichText* doc;
|
---|
165 | #endif
|
---|
166 | QString anchor;
|
---|
167 | bool pressed;
|
---|
168 | QWidget* widget;
|
---|
169 | };
|
---|
170 |
|
---|
171 |
|
---|
172 | class QWhatsThisPrivate: public QObject
|
---|
173 | {
|
---|
174 | Q_OBJECT
|
---|
175 | public:
|
---|
176 |
|
---|
177 | // an item for storing texts
|
---|
178 | struct WhatsThisItem: public QShared
|
---|
179 | {
|
---|
180 | WhatsThisItem(): QShared() { whatsthis = 0; }
|
---|
181 | ~WhatsThisItem();
|
---|
182 | QString s;
|
---|
183 | QWhatsThis* whatsthis;
|
---|
184 | };
|
---|
185 |
|
---|
186 | // the (these days pretty small) state machine
|
---|
187 | enum State { Inactive, Waiting };
|
---|
188 |
|
---|
189 | QWhatsThisPrivate();
|
---|
190 | ~QWhatsThisPrivate();
|
---|
191 |
|
---|
192 | bool eventFilter( QObject *, QEvent * );
|
---|
193 |
|
---|
194 | WhatsThisItem* newItem( QWidget * widget );
|
---|
195 | void add( QWidget * widget, QWhatsThis* special );
|
---|
196 | void add( QWidget * widget, const QString& text );
|
---|
197 |
|
---|
198 | // say it.
|
---|
199 | void say( QWidget *, const QString&, const QPoint& );
|
---|
200 |
|
---|
201 | // setup and teardown
|
---|
202 | static void setUpWhatsThis();
|
---|
203 |
|
---|
204 | void enterWhatsThisMode();
|
---|
205 | void leaveWhatsThisMode();
|
---|
206 |
|
---|
207 | // variables
|
---|
208 | QWhatsThat * whatsThat;
|
---|
209 | QPtrDict<WhatsThisItem> * dict;
|
---|
210 | QPtrDict<QWidget> * tlw;
|
---|
211 | QPtrDict<QWhatsThisButton> * buttons;
|
---|
212 | State state;
|
---|
213 |
|
---|
214 | private slots:
|
---|
215 | void cleanupWidget()
|
---|
216 | {
|
---|
217 | const QObject* o = sender();
|
---|
218 | if ( o->isWidgetType() ) // sanity
|
---|
219 | QWhatsThis::remove((QWidget*)o);
|
---|
220 | }
|
---|
221 |
|
---|
222 | };
|
---|
223 |
|
---|
224 | // static, but static the less-typing way
|
---|
225 | static QWhatsThisPrivate * wt = 0;
|
---|
226 |
|
---|
227 | // shadowWidth not const, for XP drop-shadow-fu turns it to 0
|
---|
228 | int shadowWidth = 6; // also used as '5' and '6' and even '8' below
|
---|
229 | const int vMargin = 8;
|
---|
230 | const int hMargin = 12;
|
---|
231 |
|
---|
232 | // Lets QPopupMenu destroy the QWhatsThat.
|
---|
233 | void qWhatsThisBDH()
|
---|
234 | {
|
---|
235 | if ( wt && wt->whatsThat )
|
---|
236 | wt->whatsThat->hide();
|
---|
237 | }
|
---|
238 |
|
---|
239 |
|
---|
240 | QWhatsThat::QWhatsThat( QWidget* w, const QString& txt, QWidget* parent, const char* name )
|
---|
241 | : QWidget( parent, name, WType_Popup ), text( txt ), pressed( FALSE ), widget( w )
|
---|
242 | {
|
---|
243 |
|
---|
244 | setBackgroundMode( NoBackground );
|
---|
245 | setPalette( QToolTip::palette() );
|
---|
246 | setMouseTracking( TRUE );
|
---|
247 | #ifndef QT_NO_CURSOR
|
---|
248 | setCursor( arrowCursor );
|
---|
249 | #endif
|
---|
250 |
|
---|
251 | if ( widget )
|
---|
252 | connect( widget, SIGNAL( destroyed() ), this, SLOT( widgetDestroyed() ) );
|
---|
253 |
|
---|
254 |
|
---|
255 | QRect r;
|
---|
256 | #ifndef QT_NO_RICHTEXT
|
---|
257 | doc = 0;
|
---|
258 | if ( QStyleSheet::mightBeRichText( text ) ) {
|
---|
259 | QFont f = QApplication::font( this );
|
---|
260 | doc = new QSimpleRichText( text, f );
|
---|
261 | doc->adjustSize();
|
---|
262 | r.setRect( 0, 0, doc->width(), doc->height() );
|
---|
263 | }
|
---|
264 | else
|
---|
265 | #endif
|
---|
266 | {
|
---|
267 | int sw = QApplication::desktop()->width() / 3;
|
---|
268 | if ( sw < 200 )
|
---|
269 | sw = 200;
|
---|
270 | else if ( sw > 300 )
|
---|
271 | sw = 300;
|
---|
272 |
|
---|
273 | r = fontMetrics().boundingRect( 0, 0, sw, 1000,
|
---|
274 | AlignAuto + AlignTop + WordBreak + ExpandTabs,
|
---|
275 | text );
|
---|
276 | }
|
---|
277 | #if defined(Q_WS_WIN)
|
---|
278 | if ( (qWinVersion()&WV_NT_based) > WV_2000 ) {
|
---|
279 | BOOL shadow;
|
---|
280 | SystemParametersInfo( SPI_GETDROPSHADOW, 0, &shadow, 0 );
|
---|
281 | shadowWidth = shadow ? 0 : 6;
|
---|
282 | }
|
---|
283 | #endif
|
---|
284 | resize( r.width() + 2*hMargin + shadowWidth, r.height() + 2*vMargin + shadowWidth );
|
---|
285 | }
|
---|
286 |
|
---|
287 | QWhatsThat::~QWhatsThat()
|
---|
288 | {
|
---|
289 | if ( wt && wt->whatsThat == this )
|
---|
290 | wt->whatsThat = 0;
|
---|
291 | #ifndef QT_NO_RICHTEXT
|
---|
292 | if ( doc )
|
---|
293 | delete doc;
|
---|
294 | #endif
|
---|
295 | }
|
---|
296 |
|
---|
297 | void QWhatsThat::hide()
|
---|
298 | {
|
---|
299 | QWidget::hide();
|
---|
300 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
301 | QAccessible::updateAccessibility( this, 0, QAccessible::ContextHelpEnd );
|
---|
302 | #endif
|
---|
303 | }
|
---|
304 |
|
---|
305 | void QWhatsThat::mousePressEvent( QMouseEvent* e )
|
---|
306 | {
|
---|
307 | pressed = TRUE;
|
---|
308 | if ( e->button() == LeftButton && rect().contains( e->pos() ) ) {
|
---|
309 | #ifndef QT_NO_RICHTEXT
|
---|
310 | if ( doc )
|
---|
311 | anchor = doc->anchorAt( e->pos() - QPoint( hMargin, vMargin) );
|
---|
312 | #endif
|
---|
313 | return;
|
---|
314 | }
|
---|
315 | hide();
|
---|
316 | }
|
---|
317 |
|
---|
318 | void QWhatsThat::mouseReleaseEvent( QMouseEvent* e )
|
---|
319 | {
|
---|
320 | if ( !pressed )
|
---|
321 | return;
|
---|
322 | #ifndef QT_NO_RICHTEXT
|
---|
323 | if ( e->button() == LeftButton && doc && rect().contains( e->pos() ) ) {
|
---|
324 | QString a = doc->anchorAt( e->pos() - QPoint( hMargin, vMargin ) );
|
---|
325 | QString href;
|
---|
326 | if ( anchor == a )
|
---|
327 | href = a;
|
---|
328 | anchor = QString::null;
|
---|
329 | if ( widget && wt && wt->dict ) {
|
---|
330 | QWhatsThisPrivate::WhatsThisItem * i = wt->dict->find( widget );
|
---|
331 | if ( i && i->whatsthis && !i->whatsthis->clicked( href ) )
|
---|
332 | return;
|
---|
333 | }
|
---|
334 | }
|
---|
335 | #endif
|
---|
336 | hide();
|
---|
337 | }
|
---|
338 |
|
---|
339 | void QWhatsThat::mouseMoveEvent( QMouseEvent* e)
|
---|
340 | {
|
---|
341 | #ifndef QT_NO_RICHTEXT
|
---|
342 | #ifndef QT_NO_CURSOR
|
---|
343 | if ( !doc )
|
---|
344 | return;
|
---|
345 | QString a = doc->anchorAt( e->pos() - QPoint( hMargin, vMargin ) );
|
---|
346 | if ( !a.isEmpty() )
|
---|
347 | setCursor( pointingHandCursor );
|
---|
348 | else
|
---|
349 | setCursor( arrowCursor );
|
---|
350 | #endif
|
---|
351 | #endif
|
---|
352 | }
|
---|
353 |
|
---|
354 |
|
---|
355 | void QWhatsThat::keyPressEvent( QKeyEvent* )
|
---|
356 | {
|
---|
357 | hide();
|
---|
358 | }
|
---|
359 |
|
---|
360 |
|
---|
361 |
|
---|
362 | void QWhatsThat::paintEvent( QPaintEvent* )
|
---|
363 | {
|
---|
364 | bool drawShadow = TRUE;
|
---|
365 | #if defined(Q_WS_WIN)
|
---|
366 | if ( (qWinVersion()&WV_NT_based) > WV_2000 ) {
|
---|
367 | BOOL shadow;
|
---|
368 | SystemParametersInfo( SPI_GETDROPSHADOW, 0, &shadow, 0 );
|
---|
369 | drawShadow = !shadow;
|
---|
370 | }
|
---|
371 | #elif defined(Q_WS_MACX)
|
---|
372 | drawShadow = FALSE; //never draw it on OS X we get it for free
|
---|
373 | #endif
|
---|
374 |
|
---|
375 | QRect r = rect();
|
---|
376 | if ( drawShadow )
|
---|
377 | r.addCoords( 0, 0, -shadowWidth, -shadowWidth );
|
---|
378 | QPainter p( this);
|
---|
379 | p.setPen( colorGroup().foreground() );
|
---|
380 | p.drawRect( r );
|
---|
381 | p.setPen( colorGroup().mid() );
|
---|
382 | p.setBrush( colorGroup().brush( QColorGroup::Background ) );
|
---|
383 | int w = r.width();
|
---|
384 | int h = r.height();
|
---|
385 | p.drawRect( 1, 1, w-2, h-2 );
|
---|
386 | if ( drawShadow ) {
|
---|
387 | p.setPen( colorGroup().shadow() );
|
---|
388 | p.drawPoint( w + 5, 6 );
|
---|
389 | p.drawLine( w + 3, 6, w + 5, 8 );
|
---|
390 | p.drawLine( w + 1, 6, w + 5, 10 );
|
---|
391 | int i;
|
---|
392 | for( i=7; i < h; i += 2 )
|
---|
393 | p.drawLine( w, i, w + 5, i + 5 );
|
---|
394 | for( i = w - i + h; i > 6; i -= 2 )
|
---|
395 | p.drawLine( i, h, i + 5, h + 5 );
|
---|
396 | for( ; i > 0 ; i -= 2 )
|
---|
397 | p.drawLine( 6, h + 6 - i, i + 5, h + 5 );
|
---|
398 | }
|
---|
399 | p.setPen( colorGroup().foreground() );
|
---|
400 | r.addCoords( hMargin, vMargin, -hMargin, -vMargin );
|
---|
401 |
|
---|
402 | #ifndef QT_NO_RICHTEXT
|
---|
403 | if ( doc ) {
|
---|
404 | doc->draw( &p, r.x(), r.y(), r, colorGroup(), 0 );
|
---|
405 | }
|
---|
406 | else
|
---|
407 | #endif
|
---|
408 | {
|
---|
409 | p.drawText( r, AlignAuto + AlignTop + WordBreak + ExpandTabs, text );
|
---|
410 | }
|
---|
411 | }
|
---|
412 |
|
---|
413 | // the item
|
---|
414 | QWhatsThisPrivate::WhatsThisItem::~WhatsThisItem()
|
---|
415 | {
|
---|
416 | if ( count )
|
---|
417 | qFatal( "QWhatsThis: Internal error (%d)", count );
|
---|
418 | delete whatsthis;
|
---|
419 | }
|
---|
420 |
|
---|
421 |
|
---|
422 | static const char * const button_image[] = {
|
---|
423 | "16 16 3 1",
|
---|
424 | " c None",
|
---|
425 | "o c #000000",
|
---|
426 | "a c #000080",
|
---|
427 | "o aaaaa ",
|
---|
428 | "oo aaa aaa ",
|
---|
429 | "ooo aaa aaa",
|
---|
430 | "oooo aa aa",
|
---|
431 | "ooooo aa aa",
|
---|
432 | "oooooo a aaa",
|
---|
433 | "ooooooo aaa ",
|
---|
434 | "oooooooo aaa ",
|
---|
435 | "ooooooooo aaa ",
|
---|
436 | "ooooo aaa ",
|
---|
437 | "oo ooo ",
|
---|
438 | "o ooo aaa ",
|
---|
439 | " ooo aaa ",
|
---|
440 | " ooo ",
|
---|
441 | " ooo ",
|
---|
442 | " ooo "};
|
---|
443 |
|
---|
444 | // the button class
|
---|
445 | QWhatsThisButton::QWhatsThisButton( QWidget * parent, const char * name )
|
---|
446 | : QToolButton( parent, name )
|
---|
447 | {
|
---|
448 | QPixmap p( (const char**)button_image );
|
---|
449 | setPixmap( p );
|
---|
450 | setToggleButton( TRUE );
|
---|
451 | setAutoRaise( TRUE );
|
---|
452 | setFocusPolicy( NoFocus );
|
---|
453 | setTextLabel( tr( "What's this?" ) );
|
---|
454 | wt->buttons->insert( (void *)this, this );
|
---|
455 | connect( this, SIGNAL( released() ),
|
---|
456 | this, SLOT( mouseReleased() ) );
|
---|
457 | }
|
---|
458 |
|
---|
459 |
|
---|
460 | QWhatsThisButton::~QWhatsThisButton()
|
---|
461 | {
|
---|
462 | if ( wt && wt->buttons )
|
---|
463 | wt->buttons->take( (void *)this );
|
---|
464 | }
|
---|
465 |
|
---|
466 |
|
---|
467 | void QWhatsThisButton::mouseReleased()
|
---|
468 | {
|
---|
469 | if ( wt->state == QWhatsThisPrivate::Inactive && isOn() ) {
|
---|
470 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
471 | #ifndef QT_NO_CURSOR
|
---|
472 | QApplication::setOverrideCursor( whatsThisCursor, FALSE );
|
---|
473 | #endif
|
---|
474 | wt->state = QWhatsThisPrivate::Waiting;
|
---|
475 | qApp->installEventFilter( wt );
|
---|
476 | }
|
---|
477 | }
|
---|
478 |
|
---|
479 | static void qWhatsThisPrivateCleanup()
|
---|
480 | {
|
---|
481 | if( wt ) {
|
---|
482 | delete wt;
|
---|
483 | wt = 0;
|
---|
484 | }
|
---|
485 | }
|
---|
486 |
|
---|
487 | // the what's this manager class
|
---|
488 | QWhatsThisPrivate::QWhatsThisPrivate()
|
---|
489 | : QObject( 0, "global what's this object" )
|
---|
490 | {
|
---|
491 | whatsThat = 0;
|
---|
492 | dict = new QPtrDict<QWhatsThisPrivate::WhatsThisItem>;
|
---|
493 | tlw = new QPtrDict<QWidget>;
|
---|
494 | wt = this;
|
---|
495 | buttons = new QPtrDict<QWhatsThisButton>;
|
---|
496 | state = Inactive;
|
---|
497 | }
|
---|
498 |
|
---|
499 | QWhatsThisPrivate::~QWhatsThisPrivate()
|
---|
500 | {
|
---|
501 | #ifndef QT_NO_CURSOR
|
---|
502 | if ( state == Waiting && qApp )
|
---|
503 | QApplication::restoreOverrideCursor();
|
---|
504 | #endif
|
---|
505 | // the two straight-and-simple dicts
|
---|
506 | delete tlw;
|
---|
507 | delete buttons;
|
---|
508 |
|
---|
509 | // then delete the complex one.
|
---|
510 | QPtrDictIterator<WhatsThisItem> it( *dict );
|
---|
511 | WhatsThisItem * i;
|
---|
512 | QWidget * w;
|
---|
513 | while( (i=it.current()) != 0 ) {
|
---|
514 | w = (QWidget *)it.currentKey();
|
---|
515 | ++it;
|
---|
516 | dict->take( w );
|
---|
517 | if ( i->deref() )
|
---|
518 | delete i;
|
---|
519 | }
|
---|
520 | delete dict;
|
---|
521 | if ( whatsThat && !whatsThat->parentWidget() ) {
|
---|
522 | delete whatsThat;
|
---|
523 | }
|
---|
524 | // and finally lose wt
|
---|
525 | wt = 0;
|
---|
526 | }
|
---|
527 |
|
---|
528 | bool QWhatsThisPrivate::eventFilter( QObject * o, QEvent * e )
|
---|
529 | {
|
---|
530 | switch( state ) {
|
---|
531 | case Waiting:
|
---|
532 | if ( e->type() == QEvent::MouseButtonPress && o->isWidgetType() ) {
|
---|
533 | QWidget * w = (QWidget *) o;
|
---|
534 | if ( ( (QMouseEvent*)e)->button() == RightButton )
|
---|
535 | return FALSE; // ignore RMB
|
---|
536 | if ( w->customWhatsThis() )
|
---|
537 | return FALSE;
|
---|
538 | QWhatsThisPrivate::WhatsThisItem * i = 0;
|
---|
539 | QMouseEvent* me = (QMouseEvent*) e;
|
---|
540 | QPoint p = me->pos();
|
---|
541 | while( w && !i ) {
|
---|
542 | i = dict->find( w );
|
---|
543 | if ( !i ) {
|
---|
544 | p += w->pos();
|
---|
545 | w = w->parentWidget( TRUE );
|
---|
546 | }
|
---|
547 | }
|
---|
548 | leaveWhatsThisMode();
|
---|
549 | if (!i ) {
|
---|
550 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
551 | QAccessible::updateAccessibility( this, 0, QAccessible::ContextHelpEnd );
|
---|
552 | #endif
|
---|
553 | return TRUE;
|
---|
554 | }
|
---|
555 | if ( i->whatsthis )
|
---|
556 | say( w, i->whatsthis->text( p ), me->globalPos() );
|
---|
557 | else
|
---|
558 | say( w, i->s, me->globalPos() );
|
---|
559 | return TRUE;
|
---|
560 | } else if ( e->type() == QEvent::MouseButtonRelease ) {
|
---|
561 | if ( ( (QMouseEvent*)e)->button() == RightButton )
|
---|
562 | return FALSE; // ignore RMB
|
---|
563 | return !o->isWidgetType() || !((QWidget*)o)->customWhatsThis();
|
---|
564 | } else if ( e->type() == QEvent::MouseMove ) {
|
---|
565 | return !o->isWidgetType() || !((QWidget*)o)->customWhatsThis();
|
---|
566 | } else if ( e->type() == QEvent::KeyPress ) {
|
---|
567 | QKeyEvent* kev = (QKeyEvent*)e;
|
---|
568 |
|
---|
569 | if ( kev->key() == Qt::Key_Escape ) {
|
---|
570 | leaveWhatsThisMode();
|
---|
571 | return TRUE;
|
---|
572 | } else if ( o->isWidgetType() && ((QWidget*)o)->customWhatsThis() ) {
|
---|
573 | return FALSE;
|
---|
574 | } else if ( kev->key() == Key_Menu ||
|
---|
575 | ( kev->key() == Key_F10 &&
|
---|
576 | kev->state() == ShiftButton ) ) {
|
---|
577 | // we don't react to these keys, they are used for context menus
|
---|
578 | return FALSE;
|
---|
579 | } else if ( kev->state() == kev->stateAfter() &&
|
---|
580 | kev->key() != Key_Meta ) { // not a modifier key
|
---|
581 | leaveWhatsThisMode();
|
---|
582 | }
|
---|
583 | } else if ( e->type() == QEvent::MouseButtonDblClick ) {
|
---|
584 | return TRUE;
|
---|
585 | }
|
---|
586 | break;
|
---|
587 | case Inactive:
|
---|
588 | if ( e->type() == QEvent::Accel &&
|
---|
589 | ((QKeyEvent *)e)->key() == Key_F1 &&
|
---|
590 | o->isWidgetType() &&
|
---|
591 | ((QKeyEvent *)e)->state() == ShiftButton ) {
|
---|
592 | QWidget * w = ((QWidget *)o)->focusWidget();
|
---|
593 | if ( !w )
|
---|
594 | break;
|
---|
595 | QString s = QWhatsThis::textFor( w, QPoint(0,0), TRUE );
|
---|
596 | if ( !s.isNull() ) {
|
---|
597 | say ( w, s, w->mapToGlobal( w->rect().center() ) );
|
---|
598 | ((QKeyEvent *)e)->accept();
|
---|
599 | return TRUE;
|
---|
600 | }
|
---|
601 | }
|
---|
602 | break;
|
---|
603 | }
|
---|
604 | return FALSE;
|
---|
605 | }
|
---|
606 |
|
---|
607 |
|
---|
608 |
|
---|
609 | void QWhatsThisPrivate::setUpWhatsThis()
|
---|
610 | {
|
---|
611 | if ( !wt ) {
|
---|
612 | wt = new QWhatsThisPrivate();
|
---|
613 |
|
---|
614 | // It is necessary to use a post routine, because
|
---|
615 | // the destructor deletes pixmaps and other stuff that
|
---|
616 | // needs a working X connection under X11.
|
---|
617 | qAddPostRoutine( qWhatsThisPrivateCleanup );
|
---|
618 | }
|
---|
619 | }
|
---|
620 |
|
---|
621 |
|
---|
622 | void QWhatsThisPrivate::enterWhatsThisMode()
|
---|
623 | {
|
---|
624 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
625 | QAccessible::updateAccessibility( this, 0, QAccessible::ContextHelpStart );
|
---|
626 | #endif
|
---|
627 | }
|
---|
628 |
|
---|
629 |
|
---|
630 | void QWhatsThisPrivate::leaveWhatsThisMode()
|
---|
631 | {
|
---|
632 | if ( state == Waiting ) {
|
---|
633 | QPtrDictIterator<QWhatsThisButton> it( *(wt->buttons) );
|
---|
634 | QWhatsThisButton * b;
|
---|
635 | while( (b=it.current()) != 0 ) {
|
---|
636 | ++it;
|
---|
637 | b->setOn( FALSE );
|
---|
638 | }
|
---|
639 | #ifndef QT_NO_CURSOR
|
---|
640 | QApplication::restoreOverrideCursor();
|
---|
641 | #endif
|
---|
642 | state = Inactive;
|
---|
643 | qApp->removeEventFilter( this );
|
---|
644 | }
|
---|
645 | }
|
---|
646 |
|
---|
647 |
|
---|
648 |
|
---|
649 | void QWhatsThisPrivate::say( QWidget * widget, const QString &text, const QPoint& ppos)
|
---|
650 | {
|
---|
651 | if ( text.isEmpty() )
|
---|
652 | return;
|
---|
653 | // make a fresh widget, and set it up
|
---|
654 | delete whatsThat;
|
---|
655 | whatsThat = new QWhatsThat(
|
---|
656 | widget, text,
|
---|
657 | #if defined(Q_WS_X11)
|
---|
658 | QApplication::desktop()->screen( widget ?
|
---|
659 | widget->x11Screen() :
|
---|
660 | QCursor::x11Screen() ),
|
---|
661 | #else
|
---|
662 | 0,
|
---|
663 | #endif
|
---|
664 | "automatic what's this? widget" );
|
---|
665 |
|
---|
666 |
|
---|
667 | // okay, now to find a suitable location
|
---|
668 |
|
---|
669 | int scr = ( widget ?
|
---|
670 | QApplication::desktop()->screenNumber( widget ) :
|
---|
671 | #if defined(Q_WS_X11)
|
---|
672 | QCursor::x11Screen()
|
---|
673 | #else
|
---|
674 | QApplication::desktop()->screenNumber( ppos )
|
---|
675 | #endif // Q_WS_X11
|
---|
676 | );
|
---|
677 | QRect screen = QApplication::desktop()->screenGeometry( scr );
|
---|
678 |
|
---|
679 | int x;
|
---|
680 | int w = whatsThat->width();
|
---|
681 | int h = whatsThat->height();
|
---|
682 | int sx = screen.x();
|
---|
683 | int sy = screen.y();
|
---|
684 |
|
---|
685 | // first try locating the widget immediately above/below,
|
---|
686 | // with nice alignment if possible.
|
---|
687 | QPoint pos;
|
---|
688 | if ( widget )
|
---|
689 | pos = widget->mapToGlobal( QPoint( 0,0 ) );
|
---|
690 |
|
---|
691 | if ( widget && w > widget->width() + 16 )
|
---|
692 | x = pos.x() + widget->width()/2 - w/2;
|
---|
693 | else
|
---|
694 | x = ppos.x() - w/2;
|
---|
695 |
|
---|
696 | // squeeze it in if that would result in part of what's this
|
---|
697 | // being only partially visible
|
---|
698 | if ( x + w + shadowWidth > sx+screen.width() )
|
---|
699 | x = (widget? (QMIN(screen.width(),
|
---|
700 | pos.x() + widget->width())
|
---|
701 | ) : screen.width() )
|
---|
702 | - w;
|
---|
703 |
|
---|
704 | if ( x < sx )
|
---|
705 | x = sx;
|
---|
706 |
|
---|
707 | int y;
|
---|
708 | if ( widget && h > widget->height() + 16 ) {
|
---|
709 | y = pos.y() + widget->height() + 2; // below, two pixels spacing
|
---|
710 | // what's this is above or below, wherever there's most space
|
---|
711 | if ( y + h + 10 > sy+screen.height() )
|
---|
712 | y = pos.y() + 2 - shadowWidth - h; // above, overlap
|
---|
713 | }
|
---|
714 | y = ppos.y() + 2;
|
---|
715 |
|
---|
716 | // squeeze it in if that would result in part of what's this
|
---|
717 | // being only partially visible
|
---|
718 | if ( y + h + shadowWidth > sy+screen.height() )
|
---|
719 | y = ( widget ? (QMIN(screen.height(),
|
---|
720 | pos.y() + widget->height())
|
---|
721 | ) : screen.height() )
|
---|
722 | - h;
|
---|
723 | if ( y < sy )
|
---|
724 | y = sy;
|
---|
725 |
|
---|
726 | whatsThat->move( x, y );
|
---|
727 | whatsThat->show();
|
---|
728 | }
|
---|
729 |
|
---|
730 | QWhatsThisPrivate::WhatsThisItem* QWhatsThisPrivate::newItem( QWidget * widget )
|
---|
731 | {
|
---|
732 | WhatsThisItem * i = dict->find( (void *)widget );
|
---|
733 | if ( i )
|
---|
734 | QWhatsThis::remove( widget );
|
---|
735 | i = new WhatsThisItem;
|
---|
736 | dict->insert( (void *)widget, i );
|
---|
737 | QWidget * t = widget->topLevelWidget();
|
---|
738 | if ( !tlw->find( (void *)t ) ) {
|
---|
739 | tlw->insert( (void *)t, t );
|
---|
740 | t->installEventFilter( this );
|
---|
741 | }
|
---|
742 | connect( widget, SIGNAL(destroyed()), this, SLOT(cleanupWidget()) );
|
---|
743 | return i;
|
---|
744 | }
|
---|
745 |
|
---|
746 | void QWhatsThisPrivate::add( QWidget * widget, QWhatsThis* special )
|
---|
747 | {
|
---|
748 | newItem( widget )->whatsthis = special;
|
---|
749 | }
|
---|
750 |
|
---|
751 | void QWhatsThisPrivate::add( QWidget * widget, const QString &text )
|
---|
752 | {
|
---|
753 | newItem( widget )->s = text;
|
---|
754 | }
|
---|
755 |
|
---|
756 |
|
---|
757 | // and finally the What's This class itself
|
---|
758 |
|
---|
759 | /*!
|
---|
760 | Adds \a text as "What's this" help for \a widget. If the text is
|
---|
761 | rich text formatted (i.e. it contains markup) it will be rendered
|
---|
762 | with the default stylesheet QStyleSheet::defaultSheet().
|
---|
763 |
|
---|
764 | The text is destroyed if the widget is later destroyed, so it need
|
---|
765 | not be explicitly removed.
|
---|
766 |
|
---|
767 | \sa remove()
|
---|
768 | */
|
---|
769 | void QWhatsThis::add( QWidget * widget, const QString &text )
|
---|
770 | {
|
---|
771 | if ( text.isEmpty() )
|
---|
772 | return; // pointless
|
---|
773 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
774 | wt->add(widget,text);
|
---|
775 | }
|
---|
776 |
|
---|
777 |
|
---|
778 | /*!
|
---|
779 | Removes the "What's this?" help associated with the \a widget.
|
---|
780 | This happens automatically if the widget is destroyed.
|
---|
781 |
|
---|
782 | \sa add()
|
---|
783 | */
|
---|
784 | void QWhatsThis::remove( QWidget * widget )
|
---|
785 | {
|
---|
786 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
787 | QWhatsThisPrivate::WhatsThisItem * i = wt->dict->find( (void *)widget );
|
---|
788 | if ( !i )
|
---|
789 | return;
|
---|
790 |
|
---|
791 | wt->dict->take( (void *)widget );
|
---|
792 |
|
---|
793 | i->deref();
|
---|
794 | if ( !i->count )
|
---|
795 | delete i;
|
---|
796 | }
|
---|
797 |
|
---|
798 |
|
---|
799 | /*!
|
---|
800 | Returns the what's this text for widget \a w or QString::null if
|
---|
801 | there is no "What's this?" help for the widget. \a pos contains
|
---|
802 | the mouse position; this is useful, for example, if you've
|
---|
803 | subclassed to make the text that is displayed position dependent.
|
---|
804 |
|
---|
805 | If \a includeParents is TRUE, parent widgets are taken into
|
---|
806 | consideration as well when looking for what's this help text.
|
---|
807 |
|
---|
808 | \sa add()
|
---|
809 | */
|
---|
810 | QString QWhatsThis::textFor( QWidget * w, const QPoint& pos, bool includeParents )
|
---|
811 | {
|
---|
812 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
813 | QWhatsThisPrivate::WhatsThisItem * i = 0;
|
---|
814 | QPoint p = pos;
|
---|
815 | while( w && !i ) {
|
---|
816 | i = wt->dict->find( w );
|
---|
817 | if ( !includeParents )
|
---|
818 | break;
|
---|
819 | if ( !i ) {
|
---|
820 | p += w->pos();
|
---|
821 | w = w->parentWidget( TRUE );
|
---|
822 | }
|
---|
823 | }
|
---|
824 | if (!i)
|
---|
825 | return QString::null;
|
---|
826 | if ( i->whatsthis )
|
---|
827 | return i->whatsthis->text( p );
|
---|
828 | return i->s;
|
---|
829 | }
|
---|
830 |
|
---|
831 |
|
---|
832 | /*!
|
---|
833 | Creates a QToolButton preconfigured to enter "What's this?" mode
|
---|
834 | when clicked. You will often use this with a tool bar as \a
|
---|
835 | parent:
|
---|
836 | \code
|
---|
837 | (void) QWhatsThis::whatsThisButton( my_help_tool_bar );
|
---|
838 | \endcode
|
---|
839 | */
|
---|
840 | QToolButton * QWhatsThis::whatsThisButton( QWidget * parent )
|
---|
841 | {
|
---|
842 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
843 | return new QWhatsThisButton( parent,
|
---|
844 | "automatic what's this? button" );
|
---|
845 | }
|
---|
846 |
|
---|
847 | /*!
|
---|
848 | Constructs a dynamic "What's this?" object for \a widget. The
|
---|
849 | object is deleted when the \a widget is destroyed.
|
---|
850 |
|
---|
851 | When the widget is queried by the user the text() function of this
|
---|
852 | QWhatsThis will be called to provide the appropriate text, rather
|
---|
853 | than using the text assigned by add().
|
---|
854 | */
|
---|
855 | QWhatsThis::QWhatsThis( QWidget * widget)
|
---|
856 | {
|
---|
857 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
858 | wt->add(widget,this);
|
---|
859 | }
|
---|
860 |
|
---|
861 |
|
---|
862 | /*!
|
---|
863 | Destroys the object and frees any allocated resources.
|
---|
864 | */
|
---|
865 | QWhatsThis::~QWhatsThis()
|
---|
866 | {
|
---|
867 | }
|
---|
868 |
|
---|
869 |
|
---|
870 | /*!
|
---|
871 | This virtual function returns the text for position \e p in the
|
---|
872 | widget that this "What's this?" object documents. If there is no
|
---|
873 | "What's this?" text for the position, QString::null is returned.
|
---|
874 |
|
---|
875 | The default implementation returns QString::null.
|
---|
876 | */
|
---|
877 | QString QWhatsThis::text( const QPoint & )
|
---|
878 | {
|
---|
879 | return QString::null;
|
---|
880 | }
|
---|
881 |
|
---|
882 | /*!
|
---|
883 | \fn bool QWhatsThis::clicked( const QString& href )
|
---|
884 |
|
---|
885 | This virtual function is called when the user clicks inside the
|
---|
886 | "What's this?" window. \a href is the link the user clicked on, or
|
---|
887 | QString::null if there was no link.
|
---|
888 |
|
---|
889 | If the function returns TRUE (the default), the "What's this?"
|
---|
890 | window is closed, otherwise it remains visible.
|
---|
891 |
|
---|
892 | The default implementation ignores \a href and returns TRUE.
|
---|
893 | */
|
---|
894 | bool QWhatsThis::clicked( const QString& )
|
---|
895 | {
|
---|
896 | return TRUE;
|
---|
897 | }
|
---|
898 |
|
---|
899 |
|
---|
900 | /*!
|
---|
901 | Enters "What's this?" mode and returns immediately.
|
---|
902 |
|
---|
903 | Qt will install a special cursor and take over mouse input until
|
---|
904 | the user clicks somewhere. It then shows any help available and
|
---|
905 | ends "What's this?" mode. Finally, Qt removes the special cursor
|
---|
906 | and help window and then restores ordinary event processing, at
|
---|
907 | which point the left mouse button is no longer pressed.
|
---|
908 |
|
---|
909 | The user can also use the Esc key to leave "What's this?" mode.
|
---|
910 |
|
---|
911 | \sa inWhatsThisMode(), leaveWhatsThisMode()
|
---|
912 | */
|
---|
913 |
|
---|
914 | void QWhatsThis::enterWhatsThisMode()
|
---|
915 | {
|
---|
916 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
917 | if ( wt->state == QWhatsThisPrivate::Inactive ) {
|
---|
918 | wt->enterWhatsThisMode();
|
---|
919 | #ifndef QT_NO_CURSOR
|
---|
920 | QApplication::setOverrideCursor( whatsThisCursor, FALSE );
|
---|
921 | #endif
|
---|
922 | wt->state = QWhatsThisPrivate::Waiting;
|
---|
923 | qApp->installEventFilter( wt );
|
---|
924 | }
|
---|
925 | }
|
---|
926 |
|
---|
927 |
|
---|
928 | /*!
|
---|
929 | Returns TRUE if the application is in "What's this?" mode;
|
---|
930 | otherwise returns FALSE.
|
---|
931 |
|
---|
932 | \sa enterWhatsThisMode(), leaveWhatsThisMode()
|
---|
933 | */
|
---|
934 | bool QWhatsThis::inWhatsThisMode()
|
---|
935 | {
|
---|
936 | if (!wt)
|
---|
937 | return FALSE;
|
---|
938 | return wt->state == QWhatsThisPrivate::Waiting;
|
---|
939 | }
|
---|
940 |
|
---|
941 |
|
---|
942 | /*!
|
---|
943 | Leaves "What's this?" question mode.
|
---|
944 |
|
---|
945 | This function is used internally by widgets that support
|
---|
946 | QWidget::customWhatsThis(); applications do not usually call it.
|
---|
947 | An example of such a widget is QPopupMenu: menus still work
|
---|
948 | normally in "What's this?" mode but also provide help texts for
|
---|
949 | individual menu items.
|
---|
950 |
|
---|
951 | If \a text is not QString::null, a "What's this?" help window is
|
---|
952 | displayed at the global screen position \a pos. If widget \a w is
|
---|
953 | not 0 and has its own dedicated QWhatsThis object, this object
|
---|
954 | will receive clicked() messages when the user clicks on hyperlinks
|
---|
955 | inside the help text.
|
---|
956 |
|
---|
957 | \sa inWhatsThisMode(), enterWhatsThisMode(), QWhatsThis::clicked()
|
---|
958 | */
|
---|
959 | void QWhatsThis::leaveWhatsThisMode( const QString& text, const QPoint& pos, QWidget* w )
|
---|
960 | {
|
---|
961 | if ( !inWhatsThisMode() )
|
---|
962 | return;
|
---|
963 |
|
---|
964 | wt->leaveWhatsThisMode();
|
---|
965 | if ( !text.isNull() )
|
---|
966 | wt->say( w, text, pos );
|
---|
967 | }
|
---|
968 |
|
---|
969 | /*!
|
---|
970 | Display \a text in a help window at the global screen position \a
|
---|
971 | pos.
|
---|
972 |
|
---|
973 | If widget \a w is not 0 and has its own dedicated QWhatsThis
|
---|
974 | object, this object will receive clicked() messages when the user
|
---|
975 | clicks on hyperlinks inside the help text.
|
---|
976 |
|
---|
977 | \sa QWhatsThis::clicked()
|
---|
978 | */
|
---|
979 | void QWhatsThis::display( const QString& text, const QPoint& pos, QWidget* w )
|
---|
980 | {
|
---|
981 | if ( inWhatsThisMode() ) {
|
---|
982 | leaveWhatsThisMode( text, pos, w );
|
---|
983 | return;
|
---|
984 | }
|
---|
985 | QWhatsThisPrivate::setUpWhatsThis();
|
---|
986 | wt->say( w, text, pos );
|
---|
987 | }
|
---|
988 |
|
---|
989 | /*!
|
---|
990 | Sets the font for all "What's this?" helps to \a font.
|
---|
991 | */
|
---|
992 | void QWhatsThis::setFont( const QFont &font )
|
---|
993 | {
|
---|
994 | QApplication::setFont( font, TRUE, "QWhatsThat" );
|
---|
995 | }
|
---|
996 |
|
---|
997 | #include "qwhatsthis.moc"
|
---|
998 | #endif
|
---|