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