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