]> git.saurik.com Git - wxWidgets.git/blame - src/common/cshelp.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / cshelp.cpp
CommitLineData
fb6261e9 1/////////////////////////////////////////////////////////////////////////////
bd83cb56 2// Name: src/common/cshelp.cpp
fb6261e9 3// Purpose: Context sensitive help class implementation
bd83cb56 4// Author: Julian Smart, Vadim Zeitlin
fb6261e9
JS
5// Modified by:
6// Created: 08/09/2000
7// RCS-ID: $Id$
bd83cb56 8// Copyright: (c) 2000 Julian Smart, Vadim Zeitlin
65571936 9// Licence: wxWindows licence
fb6261e9
JS
10/////////////////////////////////////////////////////////////////////////////
11
bd83cb56
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
bd83cb56
VZ
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
fb6261e9
JS
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
bd83cb56 24 #pragma hdrstop
fb6261e9
JS
25#endif
26
bd83cb56
VZ
27#if wxUSE_HELP
28
fb6261e9 29#ifndef WX_PRECOMP
670f9935 30 #include "wx/app.h"
02761f6c 31 #include "wx/module.h"
fb6261e9
JS
32#endif
33
afb02ca5 34#include "wx/tipwin.h"
fb6261e9
JS
35#include "wx/cshelp.h"
36
392c5133
VZ
37#if wxUSE_MS_HTML_HELP
38 #include "wx/msw/helpchm.h" // for ShowContextHelpPopup
39 #include "wx/utils.h" // for wxGetMousePosition()
40#endif
41
bd83cb56
VZ
42// ----------------------------------------------------------------------------
43// wxContextHelpEvtHandler private class
44// ----------------------------------------------------------------------------
fb6261e9 45
bd83cb56
VZ
46// This class exists in order to eat events until the left mouse button is
47// pressed
fb6261e9
JS
48class wxContextHelpEvtHandler: public wxEvtHandler
49{
50public:
51 wxContextHelpEvtHandler(wxContextHelp* contextHelp)
52 {
53 m_contextHelp = contextHelp;
54 }
55
56 virtual bool ProcessEvent(wxEvent& event);
57
58//// Data
59 wxContextHelp* m_contextHelp;
22f3361e 60
c0c133e1 61 wxDECLARE_NO_COPY_CLASS(wxContextHelpEvtHandler);
fb6261e9
JS
62};
63
bd83cb56
VZ
64// ============================================================================
65// implementation
66// ============================================================================
67
68// ----------------------------------------------------------------------------
69// wxContextHelp
70// ----------------------------------------------------------------------------
71
72/*
73 * Invokes context-sensitive help
74 */
75
76
fb6261e9
JS
77IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
78
79wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
80{
c9d59ee7 81 m_inHelp = false;
fb6261e9
JS
82
83 if (beginHelp)
84 BeginContextHelp(win);
85}
86
87wxContextHelp::~wxContextHelp()
88{
89 if (m_inHelp)
90 EndContextHelp();
91}
92
449d48f9
JS
93// Not currently needed, but on some systems capture may not work as
94// expected so we'll leave it here for now.
ecb9c007 95#ifdef __WXMOTIF__
b8b9d8a7
JS
96static void wxPushOrPopEventHandlers(wxContextHelp* help, wxWindow* win, bool push)
97{
98 if (push)
99 win->PushEventHandler(new wxContextHelpEvtHandler(help));
100 else
c9d59ee7 101 win->PopEventHandler(true);
b8b9d8a7 102
094112e9 103 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
b8b9d8a7
JS
104 while (node)
105 {
ecb9c007 106 wxWindow* child = node->GetData();
b8b9d8a7
JS
107 wxPushOrPopEventHandlers(help, child, push);
108
ecb9c007 109 node = node->GetNext();
b8b9d8a7
JS
110 }
111}
449d48f9 112#endif
b8b9d8a7 113
fb6261e9
JS
114// Begin 'context help mode'
115bool wxContextHelp::BeginContextHelp(wxWindow* win)
116{
117 if (!win)
118 win = wxTheApp->GetTopWindow();
119 if (!win)
c9d59ee7 120 return false;
fb6261e9
JS
121
122 wxCursor cursor(wxCURSOR_QUESTION_ARROW);
123 wxCursor oldCursor = win->GetCursor();
124 win->SetCursor(cursor);
125
ddcbe732
JS
126#ifdef __WXMAC__
127 wxSetCursor(cursor);
fb6261e9
JS
128#endif
129
c9d59ee7 130 m_status = false;
b8b9d8a7 131
ecb9c007 132#ifdef __WXMOTIF__
c9d59ee7 133 wxPushOrPopEventHandlers(this, win, true);
ecb9c007 134#else
449d48f9 135 win->PushEventHandler(new wxContextHelpEvtHandler(this));
ecb9c007 136#endif
fb6261e9
JS
137
138 win->CaptureMouse();
139
140 EventLoop();
141
142 win->ReleaseMouse();
143
ecb9c007 144#ifdef __WXMOTIF__
c9d59ee7 145 wxPushOrPopEventHandlers(this, win, false);
ecb9c007 146#else
c9d59ee7 147 win->PopEventHandler(true);
ecb9c007 148#endif
fb6261e9
JS
149
150 win->SetCursor(oldCursor);
151
ddcbe732
JS
152#ifdef __WXMAC__
153 wxSetCursor(wxNullCursor);
154#endif
155
fb6261e9
JS
156 if (m_status)
157 {
158 wxPoint pt;
159 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
ecb9c007
MB
160
161#if 0
d1c8aaa3
JS
162 if (winAtPtr)
163 {
ecb9c007
MB
164 printf("Picked %s (%d)\n", winAtPtr->GetName().c_str(),
165 winAtPtr->GetId());
d1c8aaa3 166 }
ecb9c007 167#endif
d1c8aaa3 168
fb6261e9
JS
169 if (winAtPtr)
170 DispatchEvent(winAtPtr, pt);
171 }
172
c9d59ee7 173 return true;
fb6261e9
JS
174}
175
176bool wxContextHelp::EndContextHelp()
177{
c9d59ee7 178 m_inHelp = false;
fb6261e9 179
c9d59ee7 180 return true;
fb6261e9
JS
181}
182
183bool wxContextHelp::EventLoop()
184{
c9d59ee7 185 m_inHelp = true;
dc4211aa 186
fb6261e9
JS
187 while ( m_inHelp )
188 {
189 if (wxTheApp->Pending())
190 {
191 wxTheApp->Dispatch();
192 }
193 else
194 {
195 wxTheApp->ProcessIdle();
196 }
197 }
dc4211aa 198
c9d59ee7 199 return true;
fb6261e9
JS
200}
201
202bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
203{
77d4384e 204 if (event.GetEventType() == wxEVT_LEFT_DOWN)
fb6261e9 205 {
c9d59ee7 206 m_contextHelp->SetStatus(true);
77d4384e 207 m_contextHelp->EndContextHelp();
c9d59ee7 208 return true;
fb6261e9 209 }
33ac7e6f 210
77d4384e
RR
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 {
c9d59ee7
WS
216 // May have already been set to true by a left-click
217 //m_contextHelp->SetStatus(false);
77d4384e 218 m_contextHelp->EndContextHelp();
c9d59ee7 219 return true;
77d4384e 220 }
33ac7e6f 221
77d4384e
RR
222 if ((event.GetEventType() == wxEVT_PAINT) ||
223 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
224 {
225 event.Skip();
c9d59ee7 226 return false;
77d4384e 227 }
33ac7e6f 228
c9d59ee7 229 return true;
fb6261e9
JS
230}
231
232// Dispatch the help event to the relevant window
233bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
234{
9a83f860 235 wxCHECK_MSG( win, false, wxT("win parameter can't be NULL") );
dc4211aa 236
1ed403dc
VZ
237 wxHelpEvent helpEvent(wxEVT_HELP, win->GetId(), pt,
238 wxHelpEvent::Origin_HelpButton);
239 helpEvent.SetEventObject(win);
dc4211aa 240
1ed403dc 241 return win->GetEventHandler()->ProcessEvent(helpEvent);
fb6261e9
JS
242}
243
bd83cb56
VZ
244// ----------------------------------------------------------------------------
245// wxContextHelpButton
246// ----------------------------------------------------------------------------
247
fb6261e9
JS
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
7a893a31
WS
254#ifndef __WXPM__
255
90350682 256static const char * csquery_xpm[] = {
fb6261e9 257"12 11 2 1",
57591e0e
JS
258" c None",
259". c #000000",
fb6261e9
JS
260" ",
261" .... ",
262" .. .. ",
263" .. .. ",
264" .. ",
265" .. ",
266" .. ",
267" ",
268" .. ",
269" .. ",
270" "};
fb6261e9 271
7a893a31
WS
272#endif
273
fb6261e9
JS
274IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
275
276BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
277 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
278END_EVENT_TABLE()
279
84bfc0d5
VZ
280wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
281 wxWindowID id,
282 const wxPoint& pos,
283 const wxSize& size,
284 long style)
dc4211aa
DW
285#if defined(__WXPM__)
286 : wxBitmapButton(parent, id, wxBitmap(wxCSQUERY_BITMAP
4ca8531f 287 ,wxBITMAP_TYPE_BMP_RESOURCE
dc4211aa
DW
288 ),
289 pos, size, style)
290#else
e0f4c2c8 291 : wxBitmapButton(parent, id, wxBitmap(csquery_xpm),
84bfc0d5 292 pos, size, style)
dc4211aa 293#endif
fb6261e9 294{
fb6261e9
JS
295}
296
33ac7e6f 297void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
fb6261e9 298{
57591e0e 299 wxContextHelp contextHelp(GetParent());
fb6261e9
JS
300}
301
bd83cb56
VZ
302// ----------------------------------------------------------------------------
303// wxHelpProvider
304// ----------------------------------------------------------------------------
305
d3b9f782 306wxHelpProvider *wxHelpProvider::ms_helpProvider = NULL;
bd83cb56
VZ
307
308// trivial implementation of some methods which we don't want to make pure
309// virtual for convenience
310
311void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
312 const wxString& WXUNUSED(text))
313{
314}
315
316void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
317 const wxString& WXUNUSED(text))
318{
319}
320
53e112a0
JS
321// removes the association
322void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
323{
324}
325
bd83cb56
VZ
326wxHelpProvider::~wxHelpProvider()
327{
328}
329
dc6588e7
VZ
330wxString wxHelpProvider::GetHelpTextMaybeAtPoint(wxWindowBase *window)
331{
332 if ( m_helptextAtPoint != wxDefaultPosition ||
333 m_helptextOrigin != wxHelpEvent::Origin_Unknown )
334 {
9a83f860 335 wxCHECK_MSG( window, wxEmptyString, wxT("window must not be NULL") );
dc6588e7
VZ
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
bd83cb56
VZ
349// ----------------------------------------------------------------------------
350// wxSimpleHelpProvider
351// ----------------------------------------------------------------------------
352
17a1ebd1
VZ
353#define WINHASH_KEY(w) wxPtrToUInt(w)
354
bd83cb56
VZ
355wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
356{
871192ce 357 wxSimpleHelpProviderHashMap::iterator it = m_hashWindows.find(WINHASH_KEY(window));
bd83cb56 358
ba8c1601
MB
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;
bd83cb56
VZ
367}
368
369void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
370{
17a1ebd1
VZ
371 m_hashWindows.erase(WINHASH_KEY(window));
372 m_hashWindows[WINHASH_KEY(window)] = text;
bd83cb56
VZ
373}
374
375void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
376{
871192ce 377 wxSimpleHelpProviderHashMap::key_type key = (wxSimpleHelpProviderHashMap::key_type)id;
65aeb571
WS
378 m_hashIds.erase(key);
379 m_hashIds[key] = text;
bd83cb56
VZ
380}
381
53e112a0
JS
382// removes the association
383void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
384{
17a1ebd1 385 m_hashWindows.erase(WINHASH_KEY(window));
53e112a0
JS
386}
387
bd83cb56
VZ
388bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
389{
392c5133 390#if wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
27f21318
VZ
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
dc6588e7 396 const wxString text = GetHelpTextMaybeAtPoint(window);
392c5133 397
bd83cb56
VZ
398 if ( !text.empty() )
399 {
392c5133
VZ
400 // use the native help popup style if it's available
401#if wxUSE_MS_HTML_HELP
402 if ( !wxCHMHelpController::ShowContextHelpPopup
403 (
404 text,
27f21318 405 posTooltip,
392c5133
VZ
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 }
bd83cb56 429
c9d59ee7 430 return true;
bd83cb56 431 }
392c5133 432#else // !wxUSE_MS_HTML_HELP && !wxUSE_TIPWINDOW
ddc80eb4 433 wxUnusedVar(window);
392c5133 434#endif // wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
bd83cb56 435
c9d59ee7 436 return false;
bd83cb56
VZ
437}
438
5100cabf
JS
439// ----------------------------------------------------------------------------
440// wxHelpControllerHelpProvider
441// ----------------------------------------------------------------------------
442
443wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
444{
445 m_helpController = hc;
446}
447
448bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
449{
dc6588e7 450 const wxString text = GetHelpTextMaybeAtPoint(window);
5100cabf 451
dc6588e7
VZ
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;
5100cabf
JS
465 }
466
dc6588e7
VZ
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);
5100cabf
JS
470}
471
472// Convenience function for turning context id into wxString
473wxString wxContextId(int id)
474{
9a83f860 475 return wxString::Format(wxT("%d"), id);
5100cabf
JS
476}
477
129caadd
JS
478// ----------------------------------------------------------------------------
479// wxHelpProviderModule: module responsible for cleaning up help provider.
480// ----------------------------------------------------------------------------
481
482class wxHelpProviderModule : public wxModule
483{
484public:
485 bool OnInit();
486 void OnExit();
487
488private:
489 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
490};
491
492IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
493
494bool 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
c9d59ee7 500 return true;
129caadd
JS
501}
502
503void wxHelpProviderModule::OnExit()
504{
505 if (wxHelpProvider::Get())
506 {
507 delete wxHelpProvider::Get();
508 wxHelpProvider::Set(NULL);
509 }
510}
511
fb6261e9 512#endif // wxUSE_HELP