]> git.saurik.com Git - wxWidgets.git/blame - src/common/cshelp.cpp
Halfway reasonable implementation of wxFont for wxCocoa.
[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
VZ
60
61 DECLARE_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
126#ifdef __WXMSW__
127 // wxSetCursor(cursor);
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
152 if (m_status)
153 {
154 wxPoint pt;
155 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
ecb9c007
MB
156
157#if 0
d1c8aaa3
JS
158 if (winAtPtr)
159 {
ecb9c007
MB
160 printf("Picked %s (%d)\n", winAtPtr->GetName().c_str(),
161 winAtPtr->GetId());
d1c8aaa3 162 }
ecb9c007 163#endif
d1c8aaa3 164
fb6261e9
JS
165 if (winAtPtr)
166 DispatchEvent(winAtPtr, pt);
167 }
168
c9d59ee7 169 return true;
fb6261e9
JS
170}
171
172bool wxContextHelp::EndContextHelp()
173{
c9d59ee7 174 m_inHelp = false;
fb6261e9 175
c9d59ee7 176 return true;
fb6261e9
JS
177}
178
179bool wxContextHelp::EventLoop()
180{
c9d59ee7 181 m_inHelp = true;
dc4211aa 182
fb6261e9
JS
183 while ( m_inHelp )
184 {
185 if (wxTheApp->Pending())
186 {
187 wxTheApp->Dispatch();
188 }
189 else
190 {
191 wxTheApp->ProcessIdle();
192 }
193 }
dc4211aa 194
c9d59ee7 195 return true;
fb6261e9
JS
196}
197
198bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
199{
77d4384e 200 if (event.GetEventType() == wxEVT_LEFT_DOWN)
fb6261e9 201 {
c9d59ee7 202 m_contextHelp->SetStatus(true);
77d4384e 203 m_contextHelp->EndContextHelp();
c9d59ee7 204 return true;
fb6261e9 205 }
33ac7e6f 206
77d4384e
RR
207 if ((event.GetEventType() == wxEVT_CHAR) ||
208 (event.GetEventType() == wxEVT_KEY_DOWN) ||
209 (event.GetEventType() == wxEVT_ACTIVATE) ||
210 (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
211 {
c9d59ee7
WS
212 // May have already been set to true by a left-click
213 //m_contextHelp->SetStatus(false);
77d4384e 214 m_contextHelp->EndContextHelp();
c9d59ee7 215 return true;
77d4384e 216 }
33ac7e6f 217
77d4384e
RR
218 if ((event.GetEventType() == wxEVT_PAINT) ||
219 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
220 {
221 event.Skip();
c9d59ee7 222 return false;
77d4384e 223 }
33ac7e6f 224
c9d59ee7 225 return true;
fb6261e9
JS
226}
227
228// Dispatch the help event to the relevant window
229bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
230{
1ed403dc 231 wxCHECK_MSG( win, false, _T("win parameter can't be NULL") );
dc4211aa 232
1ed403dc
VZ
233 wxHelpEvent helpEvent(wxEVT_HELP, win->GetId(), pt,
234 wxHelpEvent::Origin_HelpButton);
235 helpEvent.SetEventObject(win);
dc4211aa 236
1ed403dc 237 return win->GetEventHandler()->ProcessEvent(helpEvent);
fb6261e9
JS
238}
239
bd83cb56
VZ
240// ----------------------------------------------------------------------------
241// wxContextHelpButton
242// ----------------------------------------------------------------------------
243
fb6261e9
JS
244/*
245 * wxContextHelpButton
246 * You can add this to your dialogs (especially on non-Windows platforms)
247 * to put the application into context help mode.
248 */
249
7a893a31
WS
250#ifndef __WXPM__
251
90350682 252static const char * csquery_xpm[] = {
fb6261e9 253"12 11 2 1",
57591e0e
JS
254" c None",
255". c #000000",
fb6261e9
JS
256" ",
257" .... ",
258" .. .. ",
259" .. .. ",
260" .. ",
261" .. ",
262" .. ",
263" ",
264" .. ",
265" .. ",
266" "};
fb6261e9 267
7a893a31
WS
268#endif
269
fb6261e9
JS
270IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
271
272BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
273 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
274END_EVENT_TABLE()
275
84bfc0d5
VZ
276wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
277 wxWindowID id,
278 const wxPoint& pos,
279 const wxSize& size,
280 long style)
dc4211aa
DW
281#if defined(__WXPM__)
282 : wxBitmapButton(parent, id, wxBitmap(wxCSQUERY_BITMAP
283 ,wxBITMAP_TYPE_RESOURCE
284 ),
285 pos, size, style)
286#else
e0f4c2c8 287 : wxBitmapButton(parent, id, wxBitmap(csquery_xpm),
84bfc0d5 288 pos, size, style)
dc4211aa 289#endif
fb6261e9 290{
fb6261e9
JS
291}
292
33ac7e6f 293void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
fb6261e9 294{
57591e0e 295 wxContextHelp contextHelp(GetParent());
fb6261e9
JS
296}
297
bd83cb56
VZ
298// ----------------------------------------------------------------------------
299// wxHelpProvider
300// ----------------------------------------------------------------------------
301
302wxHelpProvider *wxHelpProvider::ms_helpProvider = (wxHelpProvider *)NULL;
303
304// trivial implementation of some methods which we don't want to make pure
305// virtual for convenience
306
307void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
308 const wxString& WXUNUSED(text))
309{
310}
311
312void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
313 const wxString& WXUNUSED(text))
314{
315}
316
53e112a0
JS
317// removes the association
318void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
319{
320}
321
bd83cb56
VZ
322wxHelpProvider::~wxHelpProvider()
323{
324}
325
dc6588e7
VZ
326wxString wxHelpProvider::GetHelpTextMaybeAtPoint(wxWindowBase *window)
327{
328 if ( m_helptextAtPoint != wxDefaultPosition ||
329 m_helptextOrigin != wxHelpEvent::Origin_Unknown )
330 {
331 wxCHECK_MSG( window, wxEmptyString, _T("window must not be NULL") );
332
333 wxPoint pt = m_helptextAtPoint;
334 wxHelpEvent::Origin origin = m_helptextOrigin;
335
336 m_helptextAtPoint = wxDefaultPosition;
337 m_helptextOrigin = wxHelpEvent::Origin_Unknown;
338
339 return window->GetHelpTextAtPoint(pt, origin);
340 }
341
342 return GetHelp(window);
343}
344
bd83cb56
VZ
345// ----------------------------------------------------------------------------
346// wxSimpleHelpProvider
347// ----------------------------------------------------------------------------
348
17a1ebd1
VZ
349#define WINHASH_KEY(w) wxPtrToUInt(w)
350
bd83cb56
VZ
351wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
352{
871192ce 353 wxSimpleHelpProviderHashMap::iterator it = m_hashWindows.find(WINHASH_KEY(window));
bd83cb56 354
ba8c1601
MB
355 if ( it == m_hashWindows.end() )
356 {
357 it = m_hashIds.find(window->GetId());
358 if ( it == m_hashIds.end() )
359 return wxEmptyString;
360 }
361
362 return it->second;
bd83cb56
VZ
363}
364
365void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
366{
17a1ebd1
VZ
367 m_hashWindows.erase(WINHASH_KEY(window));
368 m_hashWindows[WINHASH_KEY(window)] = text;
bd83cb56
VZ
369}
370
371void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
372{
871192ce 373 wxSimpleHelpProviderHashMap::key_type key = (wxSimpleHelpProviderHashMap::key_type)id;
65aeb571
WS
374 m_hashIds.erase(key);
375 m_hashIds[key] = text;
bd83cb56
VZ
376}
377
53e112a0
JS
378// removes the association
379void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
380{
17a1ebd1 381 m_hashWindows.erase(WINHASH_KEY(window));
53e112a0
JS
382}
383
bd83cb56
VZ
384bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
385{
392c5133 386#if wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
dc6588e7 387 const wxString text = GetHelpTextMaybeAtPoint(window);
392c5133 388
bd83cb56
VZ
389 if ( !text.empty() )
390 {
392c5133
VZ
391 // use the native help popup style if it's available
392#if wxUSE_MS_HTML_HELP
393 if ( !wxCHMHelpController::ShowContextHelpPopup
394 (
395 text,
396 wxGetMousePosition(),
397 (wxWindow *)window
398 ) )
399#endif // wxUSE_MS_HTML_HELP
400 {
401#if wxUSE_TIPWINDOW
402 static wxTipWindow* s_tipWindow = NULL;
403
404 if ( s_tipWindow )
405 {
406 // Prevent s_tipWindow being nulled in OnIdle, thereby removing
407 // the chance for the window to be closed by ShowHelp
408 s_tipWindow->SetTipWindowPtr(NULL);
409 s_tipWindow->Close();
410 }
411
412 s_tipWindow = new wxTipWindow((wxWindow *)window, text,
413 100, &s_tipWindow);
414#else // !wxUSE_TIPWINDOW
415 // we tried wxCHMHelpController but it failed and we don't have
416 // wxTipWindow to fall back on, so
417 return false;
418#endif // wxUSE_TIPWINDOW
419 }
bd83cb56 420
c9d59ee7 421 return true;
bd83cb56 422 }
392c5133 423#else // !wxUSE_MS_HTML_HELP && !wxUSE_TIPWINDOW
ddc80eb4 424 wxUnusedVar(window);
392c5133 425#endif // wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
bd83cb56 426
c9d59ee7 427 return false;
bd83cb56
VZ
428}
429
5100cabf
JS
430// ----------------------------------------------------------------------------
431// wxHelpControllerHelpProvider
432// ----------------------------------------------------------------------------
433
434wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
435{
436 m_helpController = hc;
437}
438
439bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
440{
dc6588e7 441 const wxString text = GetHelpTextMaybeAtPoint(window);
5100cabf 442
dc6588e7
VZ
443 if ( text.empty() )
444 return false;
445
446 if ( m_helpController )
447 {
448 // if it's a numeric topic, show it
449 long topic;
450 if ( text.ToLong(&topic) )
451 return m_helpController->DisplayContextPopup(topic);
452
453 // otherwise show the text directly
454 if ( m_helpController->DisplayTextPopup(text, wxGetMousePosition()) )
455 return true;
5100cabf
JS
456 }
457
dc6588e7
VZ
458 // if there is no help controller or it's not capable of showing the help,
459 // fallback to the default method
460 return wxSimpleHelpProvider::ShowHelp(window);
5100cabf
JS
461}
462
463// Convenience function for turning context id into wxString
464wxString wxContextId(int id)
465{
11968fef 466 return wxString::Format(_T("%d"), id);
5100cabf
JS
467}
468
129caadd
JS
469// ----------------------------------------------------------------------------
470// wxHelpProviderModule: module responsible for cleaning up help provider.
471// ----------------------------------------------------------------------------
472
473class wxHelpProviderModule : public wxModule
474{
475public:
476 bool OnInit();
477 void OnExit();
478
479private:
480 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
481};
482
483IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
484
485bool wxHelpProviderModule::OnInit()
486{
487 // Probably we don't want to do anything by default,
488 // since it could pull in extra code
489 // wxHelpProvider::Set(new wxSimpleHelpProvider);
490
c9d59ee7 491 return true;
129caadd
JS
492}
493
494void wxHelpProviderModule::OnExit()
495{
496 if (wxHelpProvider::Get())
497 {
498 delete wxHelpProvider::Get();
499 wxHelpProvider::Set(NULL);
500 }
501}
502
fb6261e9 503#endif // wxUSE_HELP