1
/* ---------------------------------------------------------------------
2
* Implementation of ContextHelp context_help.cpp
3
* Context-sensitive help
4
* ---------------------------------------------------------------------
5
* This file is part of Valkyrie, a front-end for Valgrind
6
* Copyright (C) 2000-2008, OpenWorks LLP <info@open-works.co.uk>
7
* This program is released under the terms of the GNU GPL v.2
8
* See the file COPYING for the full license details.
11
#include "context_help.h"
12
#include "hand_book.h"
13
#include "vk_utils.h" // VK_DEBUG
15
#include <qapplication.h>
18
static const char * const context_help_xpm[] = {
41
/* static, but static the less-typing way */
42
static ContextHelp * ctxt = 0;
45
/* class ContextHelpButton --------------------------------------------- */
46
ContextHelpButton::~ContextHelpButton()
48
if ( ctxt && ctxt->buttons ) {
49
ctxt->buttons->take( (void*)this );
54
ContextHelpButton::ContextHelpButton( QWidget* parent, HandBook* book )
55
: QToolButton( parent, "ctxt_help_tb" )
59
ctxt->buttons->insert( (void*)this, this );
62
QPixmap p( (const char**)context_help_xpm );
64
setToggleButton( true );
66
setFocusPolicy( NoFocus );
67
setTextLabel( "Context Help" );
69
connect( this, SIGNAL( released() ),
70
this, SLOT( mouseReleased() ) );
74
void ContextHelpButton::mouseReleased()
76
if ( ctxt->state == ContextHelp::Inactive && isOn() ) {
78
QApplication::setOverrideCursor( whatsThisCursor, false );
79
ctxt->state = ContextHelp::Waiting;
80
qApp->installEventFilter( ctxt );
87
/* class ContextHelp --------------------------------------------------- */
88
static void qContextHelpCleanup()
97
ContextHelp::UrlItem::~UrlItem()
100
VK_DEBUG("Internal error (%d)", count);
105
ContextHelp::ContextHelp()
106
: QObject( 0, "global context help" )
110
wdict = new QPtrDict<ContextHelp::UrlItem>;
111
tlw = new QPtrDict<QWidget>;
112
buttons = new QPtrDict<ContextHelpButton>;
116
ContextHelp::~ContextHelp()
118
if ( state == Waiting && qApp )
119
QApplication::restoreOverrideCursor();
121
/* delete the two straight-and-simple dicts */
125
/* then delete the complex one. */
126
QPtrDictIterator<UrlItem> it( *wdict );
129
while( ( item = it.current() ) != 0 ) {
130
w = (QWidget *)it.currentKey();
142
/* removes the Context help associated with the widget.
143
this happens automatically if the widget is destroyed. */
144
void ContextHelp::remove( QWidget * widget )
147
ContextHelp::UrlItem * i = wdict->find( (void *)widget );
151
wdict->take( (void *)widget );
158
bool ContextHelp::eventFilter( QObject * obj, QEvent * ev )
163
if ( ev->type() == QEvent::MouseButtonPress &&
164
obj->isWidgetType() ) {
165
QWidget * w = (QWidget *) obj;
166
if ( ( (QMouseEvent*)ev)->button() == RightButton )
167
return false; /* ignore RMB */
168
ContextHelp::UrlItem * item = 0;
169
QMouseEvent* me = (QMouseEvent*) ev;
170
QPoint p = me->pos();
171
while ( w && !item ) {
172
if (w->isA("QMenuBar")) {
173
/* If we're a qmenubar, allow event to pass on so menus work... */
174
// TODO: find what menuitem we're sitting on, if any, and get that widget...
177
item = wdict->find( w );
180
w = w->parentWidget( true );
188
} else if ( ev->type() == QEvent::MouseButtonRelease ) {
189
if ( ( (QMouseEvent*)ev)->button() == RightButton )
190
return false; /* ignore RMB */
191
return !obj->isWidgetType();
192
} else if ( ev->type() == QEvent::MouseMove ) {
193
return !obj->isWidgetType();
194
} else if ( ev->type() == QEvent::KeyPress ) {
195
QKeyEvent* kev = (QKeyEvent*)ev;
196
if ( kev->key() == Qt::Key_Escape ) {
199
} else if ( kev->key() == Key_Menu ||
200
( kev->key() == Key_F10 &&
201
kev->state() == ShiftButton ) ) {
202
/* don't react to these keys: they are used for context
205
} else if ( kev->state() == kev->stateAfter() &&
206
kev->key() != Key_Meta ) {
207
/* not a modifier key */
210
} else if ( ev->type() == QEvent::MouseButtonDblClick ) {
223
void ContextHelp::setUp()
226
ctxt = new ContextHelp();
228
/* it is necessary to use a post routine, because the destructor
229
deletes pixmaps and other stuff that needs a working X
230
connection under X11. */
231
qAddPostRoutine( qContextHelpCleanup );
236
void ContextHelp::shutDown()
238
if ( state == Waiting ) {
239
QPtrDictIterator<ContextHelpButton> it( *(ctxt->buttons) );
240
ContextHelpButton * b;
241
while( ( b=it.current()) != 0 ) {
245
QApplication::restoreOverrideCursor();
247
qApp->removeEventFilter( this );
252
void ContextHelp::say( QWidget* widget, const QString &text )
254
if ( text.isEmpty() || !widget )
257
if ( !hbook->isVisible() ) {
259
/* find out where MainWindow is, and park up beside it */
260
QWidget * mw = qApp->mainWidget();
261
int scr = QApplication::desktop()->screenNumber( mw );
262
QRect screen = QApplication::desktop()->screenGeometry( scr );
265
int hw = hbook->width();
267
/* get the global co-ords of the top-left pixel of MainWin */
268
QPoint pos = mw->mapToGlobal( QPoint( 0,0 ) );
269
if ( hw < ( pos.x() - screen.x() ) )
272
x = pos.x() + mw->width();
274
hbook->move( x, pos.y() );
279
hbook->openUrl( text );
283
void ContextHelp::cleanupWidget()
285
const QObject* obj = sender();
286
if ( obj->isWidgetType() ) { /* sanity check */
287
remove( (QWidget*)obj );
292
void ContextHelp::newItem( QWidget* widget, const QString& url )
294
UrlItem* item = wdict->find( (void *)widget );
299
wdict->insert( (void*)widget, item );
300
QWidget* t = widget->topLevelWidget();
301
if ( !tlw->find( (void*)t ) ) {
302
tlw->insert( (void*)t, t );
303
t->installEventFilter( this );
306
connect( widget, SIGNAL(destroyed()),
307
this, SLOT(cleanupWidget()) );
313
/* adds url as context help for this widget.
314
the text is destroyed if the widget is later destroyed, so it need
315
not be explicitly removed. */
316
void ContextHelp::add( QWidget* widget, const QString& url )
318
vk_assert( widget != NULL );
319
if ( !url.isEmpty() ) {
321
ctxt->newItem( widget, url );