1
/****************************************************************************
2
** ContextHelp implementation
3
** - context-sensitive help
4
** --------------------------------------------------------------------------
6
** Copyright (C) 2000-2010, OpenWorks LLP. All rights reserved.
7
** <info@open-works.co.uk>
9
** This file is part of Valkyrie, a front-end for Valgrind.
11
** This file may be used under the terms of the GNU General Public
12
** License version 2.0 as published by the Free Software Foundation
13
** and appearing in the file COPYING included in the packaging of
16
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
17
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19
****************************************************************************/
21
#include <QApplication>
22
#include <QDesktopWidget>
23
#include <QMouseEvent>
26
#include "help/help_context.h"
27
#include "help/help_handbook.h"
28
#include "utils/vk_utils.h" // VK_DEBUG
31
// There can be only one.
32
static ContextHelp* ctxt = 0;
37
class ContextHelpAction
39
ContextHelpAction::~ContextHelpAction()
42
ctxt->actions.removeAll( this ); // remove widget from list
43
// TODO: check return value - num removed should only ever be 1.
48
ContextHelpAction::ContextHelpAction( QWidget* parent, HandBook* book )
51
setObjectName( QString::fromUtf8( "ctxt_help_tb" ) );
53
ContextHelp::setupSingleton();
55
ctxt->actions.append( this );
58
setIcon( QPixmap( QString::fromUtf8( ":/vk_icons/icons/context_help.xpm" ) ) );
60
setIconVisibleInMenu( true );
63
setFocusPolicy( Qt::NoFocus );
65
this->setToolTip( "This is a <b>Context Help</b> button. Clicking on a widget will open the relevant manual help page" );
67
connect( this, SIGNAL( triggered( bool ) ),
68
this, SLOT( startListening( bool ) ) );
74
- start global listen for a left mouse click on a widget
76
void ContextHelpAction::startListening( bool checked )
78
// if not already active && this button is on...
79
if ( !ctxt->listeningForEvent && checked ) {
80
QApplication::setOverrideCursor( Qt::WhatsThisCursor );
81
ctxt->listeningForEvent = true;
82
qApp->installEventFilter( ctxt );
93
static void qContextHelpCleanup()
102
ContextHelp::ContextHelp()
105
setObjectName( QString::fromUtf8( "ctxt_help_tb" ) );
107
listeningForEvent = false;
111
ContextHelp::~ContextHelp()
113
if ( listeningForEvent == true && qApp ) {
114
QApplication::restoreOverrideCursor();
125
removes the Context help associated with the widget.
126
this happens automatically if the widget is destroyed.
128
void ContextHelp::remove( QWidget* widget )
130
vk_assert( widget != NULL );
132
wdict.remove( widget );
133
// TODO: check return value - num removed should only ever be 1.
137
bool ContextHelp::eventFilter( QObject* obj, QEvent* ev )
139
if ( listeningForEvent ) {
141
if ( ev->type() == QEvent::MouseButtonPress && obj->isWidgetType() ) {
142
QWidget* widg = ( QWidget* ) obj;
144
if ((( QMouseEvent* )ev )->button() == Qt::RightButton ) {
145
return false; // ignore right mouse button
150
while ( widg && url.isEmpty() ) {
151
if ( widg->inherits( "QMenuBar" ) ) {
152
// If we're a qmenubar, allow event to pass on so menus work...
153
// TODO: find what menuitem we're sitting on, if any, and get that widget...
157
url = wdict.value( widg );
159
if ( url.isEmpty() ) {
160
// pos += widg->pos();
161
widg = widg->parentWidget(); // 0 if no parent
167
if ( !widg || url.isEmpty() ) {
169
cerr << "No help found for this widget" << endl;
176
else if ( ev->type() == QEvent::MouseButtonRelease ) {
177
if ((( QMouseEvent* )ev )->button() == Qt::RightButton ) {
178
return false; // ignore right mouse button
181
return !obj->isWidgetType();
183
else if ( ev->type() == QEvent::MouseMove ) {
184
return !obj->isWidgetType();
186
else if ( ev->type() == QEvent::KeyPress ) {
187
QKeyEvent* kev = ( QKeyEvent* )ev;
189
if ( kev->key() == Qt::Key_Escape ) {
193
else if ( kev->key() == Qt::Key_Menu ||
194
( kev->key() == Qt::Key_F10 &&
195
( kev->modifiers() & Qt::ShiftModifier ) ) ) {
196
//TODO: test shift-F10. modifiers() may not be trustworthy.
198
// don't react to these keys: they are used for context menus
202
#if 0 // TODO: how to do this in Qt4?
203
else if ( kev->state() == kev->stateAfter() &&
204
kev->key() != Qt::Key_Meta ) {
205
// not a modifier key
211
else if ( ev->type() == QEvent::MouseButtonDblClick ) {
220
void ContextHelp::setupSingleton()
223
ctxt = new ContextHelp();
226
//TODO: this really necessary?
227
// not better to setup a parent, so gets auto-deleted at app close?
228
// or just create and delete in main()?
230
/* it is necessary to use a post routine, because the destructor
231
deletes pixmaps and other stuff that needs a working X
232
connection under X11. */
233
qAddPostRoutine( qContextHelpCleanup );
239
Cancel the context help
240
- reset actions, cursor, remove eventfilter
242
void ContextHelp::cancelHelpEvent()
244
if ( listeningForEvent ) {
245
// set all actions off.
246
foreach( ContextHelpAction * act, ctxt->actions ) {
247
act->setChecked( false );
250
QApplication::restoreOverrideCursor();
251
listeningForEvent = false;
252
qApp->removeEventFilter( this );
260
void ContextHelp::showHelp( const QString& text )
262
cerr << "ContextHelp::showHelp: '" << text.toLatin1().constData() << "'" << endl;
264
if ( text.isEmpty() ) {
268
if ( !hbook->isVisible() ) {
270
// find out where MainWindow is, and park up beside it
271
QWidget* mw = qApp->activeWindow();
272
int scr = QApplication::desktop()->screenNumber( mw );
273
QRect screen = QApplication::desktop()->screenGeometry( scr );
276
int hw = hbook->width();
278
// get the global co-ords of the top-left pixel of MainWin
279
QPoint pos = mw->mapToGlobal( QPoint( 0, 0 ) );
281
if ( hw < ( pos.x() - screen.x() ) ) {
285
x = pos.x() + mw->width();
288
hbook->move( x, pos.y() );
293
hbook->openUrl( text );
298
Only of our registed widgets died: remove it from the list
300
void ContextHelp::cleanupWidget()
302
const QObject* obj = sender();
304
if ( !obj->isWidgetType() ) { // sanity check
305
cerr << "Error: ContextHelp::cleanupWidget(): "
306
<< "signal received from non-widget: "
307
<< qPrintable( obj->objectName() ) << endl;
310
remove(( QWidget* )obj );
316
Add help url to widget
318
void ContextHelp::newItem( QWidget* widg, const QString& url )
320
// in case widg already in our lists, replace it.
321
if ( wdict.contains( widg ) ) {
322
cerr << "ContextHelp::newItem(): widg ("
323
<< qPrintable( widg->objectName() ) << ") was registered to: '"
324
<< qPrintable( wdict.value( widg ) ) << "'" << endl
325
<< " - Replacing with: '" << qPrintable( url ) << "'" << endl;
326
wdict.remove( widg );
330
wdict.insert( widg, url );
332
// make sure to remove mappings that no longer exist.
333
connect( widg, SIGNAL( destroyed() ),
334
this, SLOT( cleanupWidget() ) );
340
- Initialise context help singleton if necessary
341
- Add help url to given widget
343
void ContextHelp::addHelp( QWidget* widg, const QString& url )
345
vk_assert( widg != NULL );
347
if ( !url.isEmpty() ) {
349
ctxt->newItem( widg, url );