Added recursive event handler pushing to fix context help problem.
[wxWidgets.git] / src / common / cshelp.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/cshelp.cpp
3 // Purpose: Context sensitive help class implementation
4 // Author: Julian Smart, Vadim Zeitlin
5 // Modified by:
6 // Created: 08/09/2000
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 Julian Smart, Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 #ifdef __GNUG__
17 #pragma implementation "cshelp.h"
18 #endif
19
20 // ----------------------------------------------------------------------------
21 // headers
22 // ----------------------------------------------------------------------------
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_HELP
32
33 #ifndef WX_PRECOMP
34 #endif
35
36 #include "wx/tipwin.h"
37 #include "wx/app.h"
38 #include "wx/module.h"
39 #include "wx/cshelp.h"
40
41 // ----------------------------------------------------------------------------
42 // wxContextHelpEvtHandler private class
43 // ----------------------------------------------------------------------------
44
45 // This class exists in order to eat events until the left mouse button is
46 // pressed
47 class wxContextHelpEvtHandler: public wxEvtHandler
48 {
49 public:
50 wxContextHelpEvtHandler(wxContextHelp* contextHelp)
51 {
52 m_contextHelp = contextHelp;
53 }
54
55 virtual bool ProcessEvent(wxEvent& event);
56
57 //// Data
58 wxContextHelp* m_contextHelp;
59 };
60
61 // ============================================================================
62 // implementation
63 // ============================================================================
64
65 // ----------------------------------------------------------------------------
66 // wxContextHelp
67 // ----------------------------------------------------------------------------
68
69 /*
70 * Invokes context-sensitive help
71 */
72
73
74 IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
75
76 wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
77 {
78 m_inHelp = FALSE;
79
80 if (beginHelp)
81 BeginContextHelp(win);
82 }
83
84 wxContextHelp::~wxContextHelp()
85 {
86 if (m_inHelp)
87 EndContextHelp();
88 }
89
90 static void wxPushOrPopEventHandlers(wxContextHelp* help, wxWindow* win, bool push)
91 {
92 if (push)
93 win->PushEventHandler(new wxContextHelpEvtHandler(help));
94 else
95 win->PopEventHandler();
96
97 wxNode* node = win->GetChildren().First();
98 while (node)
99 {
100 wxWindow* child = (wxWindow*) node->Data();
101 wxPushOrPopEventHandlers(help, child, push);
102
103 node = node->Next();
104 }
105 }
106
107 // Begin 'context help mode'
108 bool wxContextHelp::BeginContextHelp(wxWindow* win)
109 {
110 if (!win)
111 win = wxTheApp->GetTopWindow();
112 if (!win)
113 return FALSE;
114
115 wxCursor cursor(wxCURSOR_QUESTION_ARROW);
116 wxCursor oldCursor = win->GetCursor();
117 win->SetCursor(cursor);
118
119 #ifdef __WXMSW__
120 // wxSetCursor(cursor);
121 #endif
122
123 m_status = FALSE;
124
125 // win->PushEventHandler(new wxContextHelpEvtHandler(this));
126 wxPushOrPopEventHandlers(this, win, TRUE);
127
128 win->CaptureMouse();
129
130 EventLoop();
131
132 win->ReleaseMouse();
133
134 // win->PopEventHandler(TRUE);
135 wxPushOrPopEventHandlers(this, win, FALSE);
136
137 win->SetCursor(oldCursor);
138
139 if (m_status)
140 {
141 wxPoint pt;
142 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
143 /*
144 if (winAtPtr)
145 {
146 wxString msg;
147 msg.Printf("Picked %s (%d)", (const char*) winAtPtr->GetName(), winAtPtr->GetId());
148 cout << msg << '\n';
149 }
150 */
151
152 if (winAtPtr)
153 DispatchEvent(winAtPtr, pt);
154 }
155
156 return TRUE;
157 }
158
159 bool wxContextHelp::EndContextHelp()
160 {
161 m_inHelp = FALSE;
162
163 return TRUE;
164 }
165
166 bool wxContextHelp::EventLoop()
167 {
168 m_inHelp = TRUE;
169 while ( m_inHelp )
170 {
171 if (wxTheApp->Pending())
172 {
173 wxTheApp->Dispatch();
174 }
175 else
176 {
177 wxTheApp->ProcessIdle();
178 }
179 }
180 return TRUE;
181 }
182
183 bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
184 {
185 if (event.GetEventType() == wxEVT_LEFT_DOWN)
186 {
187 m_contextHelp->SetStatus(TRUE);
188 m_contextHelp->EndContextHelp();
189 return TRUE;
190 }
191
192 if ((event.GetEventType() == wxEVT_CHAR) ||
193 (event.GetEventType() == wxEVT_KEY_DOWN) ||
194 (event.GetEventType() == wxEVT_ACTIVATE) ||
195 (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
196 {
197 // May have already been set to TRUE by a left-click
198 //m_contextHelp->SetStatus(FALSE);
199 m_contextHelp->EndContextHelp();
200 return TRUE;
201 }
202
203 if ((event.GetEventType() == wxEVT_PAINT) ||
204 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
205 {
206 event.Skip();
207 return FALSE;
208 }
209
210 return TRUE;
211 }
212
213 // Dispatch the help event to the relevant window
214 bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
215 {
216 wxWindow* subjectOfHelp = win;
217 bool eventProcessed = FALSE;
218 while (subjectOfHelp && !eventProcessed)
219 {
220 wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), pt) ;
221 helpEvent.SetEventObject(this);
222 eventProcessed = win->GetEventHandler()->ProcessEvent(helpEvent);
223
224 // Go up the window hierarchy until the event is handled (or not).
225 // I.e. keep submitting ancestor windows until one is recognised
226 // by the app code that processes the ids and displays help.
227 subjectOfHelp = subjectOfHelp->GetParent();
228 }
229 return eventProcessed;
230 }
231
232 // ----------------------------------------------------------------------------
233 // wxContextHelpButton
234 // ----------------------------------------------------------------------------
235
236 /*
237 * wxContextHelpButton
238 * You can add this to your dialogs (especially on non-Windows platforms)
239 * to put the application into context help mode.
240 */
241
242 #if !defined(__WXMSW__)
243 static const char * csquery_xpm[] = {
244 "12 11 2 1",
245 " c None",
246 ". c #000000",
247 " ",
248 " .... ",
249 " .. .. ",
250 " .. .. ",
251 " .. ",
252 " .. ",
253 " .. ",
254 " ",
255 " .. ",
256 " .. ",
257 " "};
258 #endif
259
260 IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
261
262 BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
263 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
264 END_EVENT_TABLE()
265
266 wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
267 wxWindowID id,
268 const wxPoint& pos,
269 const wxSize& size,
270 long style)
271 : wxBitmapButton(parent, id, wxBITMAP(csquery),
272 pos, size, style)
273 {
274 }
275
276 void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
277 {
278 wxContextHelp contextHelp(GetParent());
279 }
280
281 // ----------------------------------------------------------------------------
282 // wxHelpProvider
283 // ----------------------------------------------------------------------------
284
285 wxHelpProvider *wxHelpProvider::ms_helpProvider = (wxHelpProvider *)NULL;
286
287 // trivial implementation of some methods which we don't want to make pure
288 // virtual for convenience
289
290 void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
291 const wxString& WXUNUSED(text))
292 {
293 }
294
295 void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
296 const wxString& WXUNUSED(text))
297 {
298 }
299
300 wxHelpProvider::~wxHelpProvider()
301 {
302 }
303
304 // ----------------------------------------------------------------------------
305 // wxSimpleHelpProvider
306 // ----------------------------------------------------------------------------
307
308 wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
309 {
310 bool wasFound;
311 wxString text = m_hashWindows.Get((long)window, &wasFound);
312 if ( !wasFound )
313 text = m_hashIds.Get(window->GetId());
314
315 return text;
316 }
317
318 void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
319 {
320 m_hashWindows.Put((long)window, text);
321 }
322
323 void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
324 {
325 m_hashIds.Put(id, text);
326 }
327
328 bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
329 {
330 #if wxUSE_TIPWINDOW
331 static wxTipWindow* s_tipWindow = NULL;
332
333 if (s_tipWindow)
334 {
335 // Prevent s_tipWindow being nulled in OnIdle,
336 // thereby removing the chance for the window to be closed by ShowHelp
337 s_tipWindow->SetTipWindowPtr(NULL);
338 s_tipWindow->Close();
339 }
340 s_tipWindow = NULL;
341
342 wxString text = GetHelp(window);
343 if ( !text.empty() )
344 {
345 s_tipWindow = new wxTipWindow((wxWindow *)window, text, 100, & s_tipWindow);
346
347 return TRUE;
348 }
349 #endif // wxUSE_TIPWINDOW
350
351 return FALSE;
352 }
353
354 // ----------------------------------------------------------------------------
355 // wxHelpControllerHelpProvider
356 // ----------------------------------------------------------------------------
357
358 wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
359 {
360 m_helpController = hc;
361 }
362
363 bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
364 {
365 wxString text = GetHelp(window);
366 if ( !text.empty() )
367 {
368 if (m_helpController)
369 {
370 if (text.IsNumber())
371 return m_helpController->DisplayContextPopup(wxAtoi(text));
372
373 // If the help controller is capable of popping up the text...
374 else if (m_helpController->DisplayTextPopup(text, wxGetMousePosition()))
375 {
376 return TRUE;
377 }
378 else
379 // ...else use the default method.
380 return wxSimpleHelpProvider::ShowHelp(window);
381 }
382 else
383 return wxSimpleHelpProvider::ShowHelp(window);
384
385 }
386
387 return FALSE;
388 }
389
390 // Convenience function for turning context id into wxString
391 wxString wxContextId(int id)
392 {
393 return wxString(IntToString(id));
394 }
395
396 // ----------------------------------------------------------------------------
397 // wxHelpProviderModule: module responsible for cleaning up help provider.
398 // ----------------------------------------------------------------------------
399
400 class wxHelpProviderModule : public wxModule
401 {
402 public:
403 bool OnInit();
404 void OnExit();
405
406 private:
407 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
408 };
409
410 IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
411
412 bool wxHelpProviderModule::OnInit()
413 {
414 // Probably we don't want to do anything by default,
415 // since it could pull in extra code
416 // wxHelpProvider::Set(new wxSimpleHelpProvider);
417
418 return TRUE;
419 }
420
421 void wxHelpProviderModule::OnExit()
422 {
423 if (wxHelpProvider::Get())
424 {
425 delete wxHelpProvider::Get();
426 wxHelpProvider::Set(NULL);
427 }
428 }
429
430 #endif // wxUSE_HELP