~ubuntu-branches/ubuntu/oneiric/valkyrie/oneiric

« back to all changes in this revision

Viewing changes to src/help/help_context.cpp

  • Committer: Package Import Robot
  • Author(s): Clint Byrum
  • Date: 2011-09-02 22:08:34 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: package-import@ubuntu.com-20110902220834-kigsixteppj9epp5
Tags: 2.0.0-0ubuntu1
* New upstream release. (LP: #635129, LP: #832886, LP: #721298)
* Standards bumped to 3.9.2, no changes required.
* d/control, d/rules: cdbs removed, dh minimal rule instead.
* d/control: build system is qmake not autotools
* d/control: bump required qt to qt4
* d/valkyrie.install: installing html docs manually as make install
  no longer does so.
* d/patches/valkyrie-2.0.0-fix-doc.dir.patch: Fix doc path to match
  policy. Also corrects LP: #588074 since the documentation link now
  works.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
** ContextHelp implementation
 
3
**  - context-sensitive help
 
4
** --------------------------------------------------------------------------
 
5
**
 
6
** Copyright (C) 2000-2010, OpenWorks LLP. All rights reserved.
 
7
** <info@open-works.co.uk>
 
8
**
 
9
** This file is part of Valkyrie, a front-end for Valgrind.
 
10
**
 
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
 
14
** this file.
 
15
**
 
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.
 
18
**
 
19
****************************************************************************/
 
20
 
 
21
#include <QApplication>
 
22
#include <QDesktopWidget>
 
23
#include <QMouseEvent>
 
24
 
 
25
 
 
26
#include "help/help_context.h"
 
27
#include "help/help_handbook.h"
 
28
#include "utils/vk_utils.h"              // VK_DEBUG
 
29
 
 
30
 
 
31
// There can be only one.
 
32
static ContextHelp* ctxt = 0;
 
33
 
 
34
 
 
35
 
 
36
/*!
 
37
  class ContextHelpAction
 
38
*/
 
39
ContextHelpAction::~ContextHelpAction()
 
40
{
 
41
   if ( ctxt ) {
 
42
      ctxt->actions.removeAll( this ); // remove widget from list
 
43
      // TODO: check return value - num removed should only ever be 1.
 
44
   }
 
45
}
 
46
 
 
47
 
 
48
ContextHelpAction::ContextHelpAction( QWidget* parent, HandBook* book )
 
49
   : QAction( parent )
 
50
{
 
51
   setObjectName( QString::fromUtf8( "ctxt_help_tb" ) );
 
52
   
 
53
   ContextHelp::setupSingleton();
 
54
   
 
55
   ctxt->actions.append( this );
 
56
   ctxt->hbook = book;
 
57
   
 
58
   setIcon( QPixmap( QString::fromUtf8( ":/vk_icons/icons/context_help.xpm" ) ) );
 
59
   setCheckable( true );
 
60
   setIconVisibleInMenu( true );
 
61
#if 0
 
62
   setAutoRaise( true );
 
63
   setFocusPolicy( Qt::NoFocus );
 
64
#endif
 
65
   this->setToolTip( "This is a <b>Context Help</b> button. Clicking on a widget will open the relevant manual help page" );
 
66
   
 
67
   connect( this, SIGNAL( triggered( bool ) ),
 
68
            this, SLOT( startListening( bool ) ) );
 
69
}
 
70
 
 
71
 
 
72
/*!
 
73
  Action triggered
 
74
   - start global listen for a left mouse click on a widget
 
75
*/
 
76
void ContextHelpAction::startListening( bool checked )
 
77
{
 
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 );
 
83
   }
 
84
}
 
85
 
 
86
 
 
87
 
 
88
 
 
89
 
 
90
/*!
 
91
 class ContextHelp
 
92
*/
 
93
static void qContextHelpCleanup()
 
94
{
 
95
   if ( ctxt ) {
 
96
      delete ctxt;
 
97
      ctxt = 0;
 
98
   }
 
99
}
 
100
 
 
101
 
 
102
ContextHelp::ContextHelp()
 
103
   : QObject( 0 )
 
104
{
 
105
   setObjectName( QString::fromUtf8( "ctxt_help_tb" ) );
 
106
   ctxt = this;
 
107
   listeningForEvent = false;
 
108
}
 
109
 
 
110
 
 
111
ContextHelp::~ContextHelp()
 
112
{
 
113
   if ( listeningForEvent == true && qApp ) {
 
114
      QApplication::restoreOverrideCursor();
 
115
   }
 
116
   
 
117
   actions.clear();
 
118
   wdict.clear();
 
119
   
 
120
   ctxt = 0;
 
121
}
 
122
 
 
123
 
 
124
/*!
 
125
  removes the Context help associated with the widget.
 
126
  this happens automatically if the widget is destroyed.
 
127
*/
 
128
void ContextHelp::remove( QWidget* widget )
 
129
{
 
130
   vk_assert( widget != NULL );
 
131
   
 
132
   wdict.remove( widget );
 
133
   // TODO: check return value - num removed should only ever be 1.
 
134
}
 
135
 
 
136
 
 
137
bool ContextHelp::eventFilter( QObject* obj, QEvent* ev )
 
138
{
 
139
   if ( listeningForEvent ) {
 
140
   
 
141
      if ( ev->type() == QEvent::MouseButtonPress && obj->isWidgetType() ) {
 
142
         QWidget* widg = ( QWidget* ) obj;
 
143
         
 
144
         if ((( QMouseEvent* )ev )->button() == Qt::RightButton ) {
 
145
            return false;   // ignore right mouse button
 
146
         }
 
147
         
 
148
         QString url;
 
149
         
 
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...
 
154
               return false;
 
155
            }
 
156
            
 
157
            url = wdict.value( widg );
 
158
            
 
159
            if ( url.isEmpty() ) {
 
160
               //             pos += widg->pos();
 
161
               widg = widg->parentWidget();  // 0 if no parent
 
162
            }
 
163
         }
 
164
         
 
165
         cancelHelpEvent();
 
166
         
 
167
         if ( !widg || url.isEmpty() ) {
 
168
            //TODO: vkMsg?
 
169
            cerr << "No help found for this widget" << endl;
 
170
            return true;
 
171
         }
 
172
         
 
173
         showHelp( url );
 
174
         return true;
 
175
      }
 
176
      else if ( ev->type() == QEvent::MouseButtonRelease ) {
 
177
         if ((( QMouseEvent* )ev )->button() == Qt::RightButton ) {
 
178
            return false;   // ignore right mouse button
 
179
         }
 
180
         
 
181
         return !obj->isWidgetType();
 
182
      }
 
183
      else if ( ev->type() == QEvent::MouseMove ) {
 
184
         return !obj->isWidgetType();
 
185
      }
 
186
      else if ( ev->type() == QEvent::KeyPress ) {
 
187
         QKeyEvent* kev = ( QKeyEvent* )ev;
 
188
         
 
189
         if ( kev->key() == Qt::Key_Escape ) {
 
190
            cancelHelpEvent();
 
191
            return true;
 
192
         }
 
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.
 
197
            
 
198
            // don't react to these keys: they are used for context menus
 
199
            return false;
 
200
         }
 
201
         
 
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
 
206
            cancelHelpEvent();
 
207
         }
 
208
         
 
209
#endif
 
210
      }
 
211
      else if ( ev->type() == QEvent::MouseButtonDblClick ) {
 
212
         return true;
 
213
      }
 
214
   }
 
215
   
 
216
   return false;
 
217
}
 
218
 
 
219
 
 
220
void ContextHelp::setupSingleton()
 
221
{
 
222
   if ( !ctxt ) {
 
223
      ctxt = new ContextHelp();
 
224
      
 
225
      
 
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()?
 
229
      
 
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 );
 
234
   }
 
235
}
 
236
 
 
237
 
 
238
/*!
 
239
  Cancel the context help
 
240
   - reset actions, cursor, remove eventfilter
 
241
*/
 
242
void ContextHelp::cancelHelpEvent()
 
243
{
 
244
   if ( listeningForEvent ) {
 
245
      // set all actions off.
 
246
      foreach( ContextHelpAction * act, ctxt->actions ) {
 
247
         act->setChecked( false );
 
248
      }
 
249
      
 
250
      QApplication::restoreOverrideCursor();
 
251
      listeningForEvent = false;
 
252
      qApp->removeEventFilter( this );
 
253
   }
 
254
}
 
255
 
 
256
 
 
257
/*!
 
258
  Open help at url
 
259
*/
 
260
void ContextHelp::showHelp( const QString& text )
 
261
{
 
262
   cerr << "ContextHelp::showHelp: '" << text.toLatin1().constData() << "'" << endl;
 
263
   
 
264
   if ( text.isEmpty() ) {
 
265
      return;
 
266
   }
 
267
   
 
268
   if ( !hbook->isVisible() ) {
 
269
   
 
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 );
 
274
      
 
275
      int x;
 
276
      int hw = hbook->width();
 
277
      
 
278
      // get the global co-ords of the top-left pixel of MainWin
 
279
      QPoint pos = mw->mapToGlobal( QPoint( 0, 0 ) );
 
280
      
 
281
      if ( hw < ( pos.x() - screen.x() ) ) {
 
282
         x = pos.x() - hw;
 
283
      }
 
284
      else {
 
285
         x = pos.x() + mw->width();
 
286
      }
 
287
      
 
288
      hbook->move( x, pos.y() );
 
289
      hbook->show();
 
290
   }
 
291
   
 
292
   hbook->raise();
 
293
   hbook->openUrl( text );
 
294
}
 
295
 
 
296
 
 
297
/*!
 
298
  Only of our registed widgets died: remove it from the list
 
299
*/
 
300
void ContextHelp::cleanupWidget()
 
301
{
 
302
   const QObject* obj = sender();
 
303
   
 
304
   if ( !obj->isWidgetType() ) {   // sanity check
 
305
      cerr << "Error: ContextHelp::cleanupWidget(): "
 
306
           << "signal received from non-widget: "
 
307
           << qPrintable( obj->objectName() ) << endl;
 
308
   }
 
309
   else {
 
310
      remove(( QWidget* )obj );
 
311
   }
 
312
}
 
313
 
 
314
 
 
315
/*!
 
316
  Add help url to widget
 
317
*/
 
318
void ContextHelp::newItem( QWidget* widg, const QString& url )
 
319
{
 
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 );
 
327
   }
 
328
   
 
329
   // add to our list
 
330
   wdict.insert( widg, url );
 
331
   
 
332
   // make sure to remove mappings that no longer exist.
 
333
   connect( widg, SIGNAL( destroyed() ),
 
334
            this, SLOT( cleanupWidget() ) );
 
335
}
 
336
 
 
337
 
 
338
/*!
 
339
 Static function:
 
340
  - Initialise context help singleton if necessary
 
341
  - Add help url to given widget
 
342
*/
 
343
void ContextHelp::addHelp( QWidget* widg, const QString& url )
 
344
{
 
345
   vk_assert( widg != NULL );
 
346
   
 
347
   if ( !url.isEmpty() ) {
 
348
      setupSingleton();
 
349
      ctxt->newItem( widg, url );
 
350
   }
 
351
}
 
352