]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/cshelp.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / cshelp.cpp
... / ...
CommitLineData
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
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;
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
77IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
78
79wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
80{
81 m_inHelp = false;
82
83 if (beginHelp)
84 BeginContextHelp(win);
85}
86
87wxContextHelp::~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__
96static 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'
115bool 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
176bool wxContextHelp::EndContextHelp()
177{
178 m_inHelp = false;
179
180 return true;
181}
182
183bool 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
202bool 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
233bool 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
256static 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
274IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
275
276BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
277 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
278END_EVENT_TABLE()
279
280wxContextHelpButton::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
297void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
298{
299 wxContextHelp contextHelp(GetParent());
300}
301
302// ----------------------------------------------------------------------------
303// wxHelpProvider
304// ----------------------------------------------------------------------------
305
306wxHelpProvider *wxHelpProvider::ms_helpProvider = NULL;
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
321// removes the association
322void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
323{
324}
325
326wxHelpProvider::~wxHelpProvider()
327{
328}
329
330wxString 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
355wxString 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
369void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
370{
371 m_hashWindows.erase(WINHASH_KEY(window));
372 m_hashWindows[WINHASH_KEY(window)] = text;
373}
374
375void 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
383void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
384{
385 m_hashWindows.erase(WINHASH_KEY(window));
386}
387
388bool 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
443wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
444{
445 m_helpController = hc;
446}
447
448bool 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
473wxString 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
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
500 return true;
501}
502
503void wxHelpProviderModule::OnExit()
504{
505 if (wxHelpProvider::Get())
506 {
507 delete wxHelpProvider::Get();
508 wxHelpProvider::Set(NULL);
509 }
510}
511
512#endif // wxUSE_HELP