]> git.saurik.com Git - wxWidgets.git/blob - src/common/cshelp.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[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 wxDECLARE_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 __WXMAC__
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 #ifdef __WXMAC__
153 wxSetCursor(wxNullCursor);
154 #endif
155
156 if (m_status)
157 {
158 wxPoint pt;
159 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
160
161 #if 0
162 if (winAtPtr)
163 {
164 printf("Picked %s (%d)\n", winAtPtr->GetName().c_str(),
165 winAtPtr->GetId());
166 }
167 #endif
168
169 if (winAtPtr)
170 DispatchEvent(winAtPtr, pt);
171 }
172
173 return true;
174 }
175
176 bool wxContextHelp::EndContextHelp()
177 {
178 m_inHelp = false;
179
180 return true;
181 }
182
183 bool wxContextHelp::EventLoop()
184 {
185 m_inHelp = true;
186
187 while ( m_inHelp )
188 {
189 if (wxTheApp->Pending())
190 {
191 wxTheApp->Dispatch();
192 }
193 else
194 {
195 wxTheApp->ProcessIdle();
196 }
197 }
198
199 return true;
200 }
201
202 bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
203 {
204 if (event.GetEventType() == wxEVT_LEFT_DOWN)
205 {
206 m_contextHelp->SetStatus(true);
207 m_contextHelp->EndContextHelp();
208 return true;
209 }
210
211 if ((event.GetEventType() == wxEVT_CHAR) ||
212 (event.GetEventType() == wxEVT_KEY_DOWN) ||
213 (event.GetEventType() == wxEVT_ACTIVATE) ||
214 (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
215 {
216 // May have already been set to true by a left-click
217 //m_contextHelp->SetStatus(false);
218 m_contextHelp->EndContextHelp();
219 return true;
220 }
221
222 if ((event.GetEventType() == wxEVT_PAINT) ||
223 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
224 {
225 event.Skip();
226 return false;
227 }
228
229 return true;
230 }
231
232 // Dispatch the help event to the relevant window
233 bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
234 {
235 wxCHECK_MSG( win, false, wxT("win parameter can't be NULL") );
236
237 wxHelpEvent helpEvent(wxEVT_HELP, win->GetId(), pt,
238 wxHelpEvent::Origin_HelpButton);
239 helpEvent.SetEventObject(win);
240
241 return win->GetEventHandler()->ProcessEvent(helpEvent);
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_BMP_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 = 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, wxT("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_MS_HTML_HELP || wxUSE_TIPWINDOW
391 #if wxUSE_MS_HTML_HELP
392 // m_helptextAtPoint will be reset by GetHelpTextMaybeAtPoint(), stash it
393 const wxPoint posTooltip = m_helptextAtPoint;
394 #endif // wxUSE_MS_HTML_HELP
395
396 const wxString text = GetHelpTextMaybeAtPoint(window);
397
398 if ( !text.empty() )
399 {
400 // use the native help popup style if it's available
401 #if wxUSE_MS_HTML_HELP
402 if ( !wxCHMHelpController::ShowContextHelpPopup
403 (
404 text,
405 posTooltip,
406 (wxWindow *)window
407 ) )
408 #endif // wxUSE_MS_HTML_HELP
409 {
410 #if wxUSE_TIPWINDOW
411 static wxTipWindow* s_tipWindow = NULL;
412
413 if ( s_tipWindow )
414 {
415 // Prevent s_tipWindow being nulled in OnIdle, thereby removing
416 // the chance for the window to be closed by ShowHelp
417 s_tipWindow->SetTipWindowPtr(NULL);
418 s_tipWindow->Close();
419 }
420
421 s_tipWindow = new wxTipWindow((wxWindow *)window, text,
422 100, &s_tipWindow);
423 #else // !wxUSE_TIPWINDOW
424 // we tried wxCHMHelpController but it failed and we don't have
425 // wxTipWindow to fall back on, so
426 return false;
427 #endif // wxUSE_TIPWINDOW
428 }
429
430 return true;
431 }
432 #else // !wxUSE_MS_HTML_HELP && !wxUSE_TIPWINDOW
433 wxUnusedVar(window);
434 #endif // wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
435
436 return false;
437 }
438
439 // ----------------------------------------------------------------------------
440 // wxHelpControllerHelpProvider
441 // ----------------------------------------------------------------------------
442
443 wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
444 {
445 m_helpController = hc;
446 }
447
448 bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
449 {
450 const wxString text = GetHelpTextMaybeAtPoint(window);
451
452 if ( text.empty() )
453 return false;
454
455 if ( m_helpController )
456 {
457 // if it's a numeric topic, show it
458 long topic;
459 if ( text.ToLong(&topic) )
460 return m_helpController->DisplayContextPopup(topic);
461
462 // otherwise show the text directly
463 if ( m_helpController->DisplayTextPopup(text, wxGetMousePosition()) )
464 return true;
465 }
466
467 // if there is no help controller or it's not capable of showing the help,
468 // fallback to the default method
469 return wxSimpleHelpProvider::ShowHelp(window);
470 }
471
472 // Convenience function for turning context id into wxString
473 wxString wxContextId(int id)
474 {
475 return wxString::Format(wxT("%d"), id);
476 }
477
478 // ----------------------------------------------------------------------------
479 // wxHelpProviderModule: module responsible for cleaning up help provider.
480 // ----------------------------------------------------------------------------
481
482 class wxHelpProviderModule : public wxModule
483 {
484 public:
485 bool OnInit();
486 void OnExit();
487
488 private:
489 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
490 };
491
492 IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
493
494 bool wxHelpProviderModule::OnInit()
495 {
496 // Probably we don't want to do anything by default,
497 // since it could pull in extra code
498 // wxHelpProvider::Set(new wxSimpleHelpProvider);
499
500 return true;
501 }
502
503 void wxHelpProviderModule::OnExit()
504 {
505 if (wxHelpProvider::Get())
506 {
507 delete wxHelpProvider::Get();
508 wxHelpProvider::Set(NULL);
509 }
510 }
511
512 #endif // wxUSE_HELP