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