Use the associated document manager, not the global one
[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 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_HELP
28
29 #ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/module.h"
32 #endif
33
34 #include "wx/tipwin.h"
35 #include "wx/cshelp.h"
36
37 #if wxUSE_MS_HTML_HELP
38 #include "wx/msw/helpchm.h" // for ShowContextHelpPopup
39 #include "wx/utils.h" // for wxGetMousePosition()
40 #endif
41
42 // ----------------------------------------------------------------------------
43 // wxContextHelpEvtHandler private class
44 // ----------------------------------------------------------------------------
45
46 // This class exists in order to eat events until the left mouse button is
47 // pressed
48 class wxContextHelpEvtHandler: public wxEvtHandler
49 {
50 public:
51 wxContextHelpEvtHandler(wxContextHelp* contextHelp)
52 {
53 m_contextHelp = contextHelp;
54 }
55
56 virtual bool ProcessEvent(wxEvent& event);
57
58 //// Data
59 wxContextHelp* m_contextHelp;
60
61 DECLARE_NO_COPY_CLASS(wxContextHelpEvtHandler)
62 };
63
64 // ============================================================================
65 // implementation
66 // ============================================================================
67
68 // ----------------------------------------------------------------------------
69 // wxContextHelp
70 // ----------------------------------------------------------------------------
71
72 /*
73 * Invokes context-sensitive help
74 */
75
76
77 IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
78
79 wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
80 {
81 m_inHelp = false;
82
83 if (beginHelp)
84 BeginContextHelp(win);
85 }
86
87 wxContextHelp::~wxContextHelp()
88 {
89 if (m_inHelp)
90 EndContextHelp();
91 }
92
93 // Not currently needed, but on some systems capture may not work as
94 // expected so we'll leave it here for now.
95 #ifdef __WXMOTIF__
96 static void wxPushOrPopEventHandlers(wxContextHelp* help, wxWindow* win, bool push)
97 {
98 if (push)
99 win->PushEventHandler(new wxContextHelpEvtHandler(help));
100 else
101 win->PopEventHandler(true);
102
103 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
104 while (node)
105 {
106 wxWindow* child = node->GetData();
107 wxPushOrPopEventHandlers(help, child, push);
108
109 node = node->GetNext();
110 }
111 }
112 #endif
113
114 // Begin 'context help mode'
115 bool wxContextHelp::BeginContextHelp(wxWindow* win)
116 {
117 if (!win)
118 win = wxTheApp->GetTopWindow();
119 if (!win)
120 return false;
121
122 wxCursor cursor(wxCURSOR_QUESTION_ARROW);
123 wxCursor oldCursor = win->GetCursor();
124 win->SetCursor(cursor);
125
126 #ifdef __WXMSW__
127 // wxSetCursor(cursor);
128 #endif
129
130 m_status = false;
131
132 #ifdef __WXMOTIF__
133 wxPushOrPopEventHandlers(this, win, true);
134 #else
135 win->PushEventHandler(new wxContextHelpEvtHandler(this));
136 #endif
137
138 win->CaptureMouse();
139
140 EventLoop();
141
142 win->ReleaseMouse();
143
144 #ifdef __WXMOTIF__
145 wxPushOrPopEventHandlers(this, win, false);
146 #else
147 win->PopEventHandler(true);
148 #endif
149
150 win->SetCursor(oldCursor);
151
152 if (m_status)
153 {
154 wxPoint pt;
155 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
156
157 #if 0
158 if (winAtPtr)
159 {
160 printf("Picked %s (%d)\n", winAtPtr->GetName().c_str(),
161 winAtPtr->GetId());
162 }
163 #endif
164
165 if (winAtPtr)
166 DispatchEvent(winAtPtr, pt);
167 }
168
169 return true;
170 }
171
172 bool wxContextHelp::EndContextHelp()
173 {
174 m_inHelp = false;
175
176 return true;
177 }
178
179 bool wxContextHelp::EventLoop()
180 {
181 m_inHelp = true;
182
183 while ( m_inHelp )
184 {
185 if (wxTheApp->Pending())
186 {
187 wxTheApp->Dispatch();
188 }
189 else
190 {
191 wxTheApp->ProcessIdle();
192 }
193 }
194
195 return true;
196 }
197
198 bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
199 {
200 if (event.GetEventType() == wxEVT_LEFT_DOWN)
201 {
202 m_contextHelp->SetStatus(true);
203 m_contextHelp->EndContextHelp();
204 return true;
205 }
206
207 if ((event.GetEventType() == wxEVT_CHAR) ||
208 (event.GetEventType() == wxEVT_KEY_DOWN) ||
209 (event.GetEventType() == wxEVT_ACTIVATE) ||
210 (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
211 {
212 // May have already been set to true by a left-click
213 //m_contextHelp->SetStatus(false);
214 m_contextHelp->EndContextHelp();
215 return true;
216 }
217
218 if ((event.GetEventType() == wxEVT_PAINT) ||
219 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
220 {
221 event.Skip();
222 return false;
223 }
224
225 return true;
226 }
227
228 // Dispatch the help event to the relevant window
229 bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
230 {
231 wxCHECK_MSG( win, false, _T("win parameter can't be NULL") );
232
233 wxHelpEvent helpEvent(wxEVT_HELP, win->GetId(), pt,
234 wxHelpEvent::Origin_HelpButton);
235 helpEvent.SetEventObject(win);
236
237 return win->GetEventHandler()->ProcessEvent(helpEvent);
238 }
239
240 // ----------------------------------------------------------------------------
241 // wxContextHelpButton
242 // ----------------------------------------------------------------------------
243
244 /*
245 * wxContextHelpButton
246 * You can add this to your dialogs (especially on non-Windows platforms)
247 * to put the application into context help mode.
248 */
249
250 #ifndef __WXPM__
251
252 static const char * csquery_xpm[] = {
253 "12 11 2 1",
254 " c None",
255 ". c #000000",
256 " ",
257 " .... ",
258 " .. .. ",
259 " .. .. ",
260 " .. ",
261 " .. ",
262 " .. ",
263 " ",
264 " .. ",
265 " .. ",
266 " "};
267
268 #endif
269
270 IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
271
272 BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
273 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
274 END_EVENT_TABLE()
275
276 wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
277 wxWindowID id,
278 const wxPoint& pos,
279 const wxSize& size,
280 long style)
281 #if defined(__WXPM__)
282 : wxBitmapButton(parent, id, wxBitmap(wxCSQUERY_BITMAP
283 ,wxBITMAP_TYPE_RESOURCE
284 ),
285 pos, size, style)
286 #else
287 : wxBitmapButton(parent, id, wxBitmap(csquery_xpm),
288 pos, size, style)
289 #endif
290 {
291 }
292
293 void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
294 {
295 wxContextHelp contextHelp(GetParent());
296 }
297
298 // ----------------------------------------------------------------------------
299 // wxHelpProvider
300 // ----------------------------------------------------------------------------
301
302 wxHelpProvider *wxHelpProvider::ms_helpProvider = (wxHelpProvider *)NULL;
303
304 // trivial implementation of some methods which we don't want to make pure
305 // virtual for convenience
306
307 void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
308 const wxString& WXUNUSED(text))
309 {
310 }
311
312 void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
313 const wxString& WXUNUSED(text))
314 {
315 }
316
317 // removes the association
318 void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
319 {
320 }
321
322 wxHelpProvider::~wxHelpProvider()
323 {
324 }
325
326 wxString wxHelpProvider::GetHelpTextMaybeAtPoint(wxWindowBase *window)
327 {
328 if ( m_helptextAtPoint != wxDefaultPosition ||
329 m_helptextOrigin != wxHelpEvent::Origin_Unknown )
330 {
331 wxCHECK_MSG( window, wxEmptyString, _T("window must not be NULL") );
332
333 wxPoint pt = m_helptextAtPoint;
334 wxHelpEvent::Origin origin = m_helptextOrigin;
335
336 m_helptextAtPoint = wxDefaultPosition;
337 m_helptextOrigin = wxHelpEvent::Origin_Unknown;
338
339 return window->GetHelpTextAtPoint(pt, origin);
340 }
341
342 return GetHelp(window);
343 }
344
345 // ----------------------------------------------------------------------------
346 // wxSimpleHelpProvider
347 // ----------------------------------------------------------------------------
348
349 #define WINHASH_KEY(w) wxPtrToUInt(w)
350
351 wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
352 {
353 wxSimpleHelpProviderHashMap::iterator it = m_hashWindows.find(WINHASH_KEY(window));
354
355 if ( it == m_hashWindows.end() )
356 {
357 it = m_hashIds.find(window->GetId());
358 if ( it == m_hashIds.end() )
359 return wxEmptyString;
360 }
361
362 return it->second;
363 }
364
365 void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
366 {
367 m_hashWindows.erase(WINHASH_KEY(window));
368 m_hashWindows[WINHASH_KEY(window)] = text;
369 }
370
371 void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
372 {
373 wxSimpleHelpProviderHashMap::key_type key = (wxSimpleHelpProviderHashMap::key_type)id;
374 m_hashIds.erase(key);
375 m_hashIds[key] = text;
376 }
377
378 // removes the association
379 void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
380 {
381 m_hashWindows.erase(WINHASH_KEY(window));
382 }
383
384 bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
385 {
386 #if wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
387 const wxString text = GetHelpTextMaybeAtPoint(window);
388
389 if ( !text.empty() )
390 {
391 // use the native help popup style if it's available
392 #if wxUSE_MS_HTML_HELP
393 if ( !wxCHMHelpController::ShowContextHelpPopup
394 (
395 text,
396 wxGetMousePosition(),
397 (wxWindow *)window
398 ) )
399 #endif // wxUSE_MS_HTML_HELP
400 {
401 #if wxUSE_TIPWINDOW
402 static wxTipWindow* s_tipWindow = NULL;
403
404 if ( s_tipWindow )
405 {
406 // Prevent s_tipWindow being nulled in OnIdle, thereby removing
407 // the chance for the window to be closed by ShowHelp
408 s_tipWindow->SetTipWindowPtr(NULL);
409 s_tipWindow->Close();
410 }
411
412 s_tipWindow = new wxTipWindow((wxWindow *)window, text,
413 100, &s_tipWindow);
414 #else // !wxUSE_TIPWINDOW
415 // we tried wxCHMHelpController but it failed and we don't have
416 // wxTipWindow to fall back on, so
417 return false;
418 #endif // wxUSE_TIPWINDOW
419 }
420
421 return true;
422 }
423 #else // !wxUSE_MS_HTML_HELP && !wxUSE_TIPWINDOW
424 wxUnusedVar(window);
425 #endif // wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
426
427 return false;
428 }
429
430 // ----------------------------------------------------------------------------
431 // wxHelpControllerHelpProvider
432 // ----------------------------------------------------------------------------
433
434 wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
435 {
436 m_helpController = hc;
437 }
438
439 bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
440 {
441 const wxString text = GetHelpTextMaybeAtPoint(window);
442
443 if ( text.empty() )
444 return false;
445
446 if ( m_helpController )
447 {
448 // if it's a numeric topic, show it
449 long topic;
450 if ( text.ToLong(&topic) )
451 return m_helpController->DisplayContextPopup(topic);
452
453 // otherwise show the text directly
454 if ( m_helpController->DisplayTextPopup(text, wxGetMousePosition()) )
455 return true;
456 }
457
458 // if there is no help controller or it's not capable of showing the help,
459 // fallback to the default method
460 return wxSimpleHelpProvider::ShowHelp(window);
461 }
462
463 // Convenience function for turning context id into wxString
464 wxString wxContextId(int id)
465 {
466 return wxString::Format(_T("%d"), id);
467 }
468
469 // ----------------------------------------------------------------------------
470 // wxHelpProviderModule: module responsible for cleaning up help provider.
471 // ----------------------------------------------------------------------------
472
473 class wxHelpProviderModule : public wxModule
474 {
475 public:
476 bool OnInit();
477 void OnExit();
478
479 private:
480 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
481 };
482
483 IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
484
485 bool wxHelpProviderModule::OnInit()
486 {
487 // Probably we don't want to do anything by default,
488 // since it could pull in extra code
489 // wxHelpProvider::Set(new wxSimpleHelpProvider);
490
491 return true;
492 }
493
494 void wxHelpProviderModule::OnExit()
495 {
496 if (wxHelpProvider::Get())
497 {
498 delete wxHelpProvider::Get();
499 wxHelpProvider::Set(NULL);
500 }
501 }
502
503 #endif // wxUSE_HELP