fix -Wundef gcc warnings about wxUSE_MS_HTML_HELP
[wxWidgets.git] / src / common / cshelp.cpp
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 #endif
32
33 #include "wx/tipwin.h"
34 #include "wx/module.h"
35 #include "wx/cshelp.h"
36
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
42 #if wxUSE_MS_HTML_HELP
43 #include "wx/msw/helpchm.h" // for ShowContextHelpPopup
44 #include "wx/utils.h" // for wxGetMousePosition()
45 #endif
46
47 // ----------------------------------------------------------------------------
48 // wxContextHelpEvtHandler private class
49 // ----------------------------------------------------------------------------
50
51 // This class exists in order to eat events until the left mouse button is
52 // pressed
53 class wxContextHelpEvtHandler: public wxEvtHandler
54 {
55 public:
56 wxContextHelpEvtHandler(wxContextHelp* contextHelp)
57 {
58 m_contextHelp = contextHelp;
59 }
60
61 virtual bool ProcessEvent(wxEvent& event);
62
63 //// Data
64 wxContextHelp* m_contextHelp;
65
66 DECLARE_NO_COPY_CLASS(wxContextHelpEvtHandler)
67 };
68
69 // ============================================================================
70 // implementation
71 // ============================================================================
72
73 // ----------------------------------------------------------------------------
74 // wxContextHelp
75 // ----------------------------------------------------------------------------
76
77 /*
78 * Invokes context-sensitive help
79 */
80
81
82 IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
83
84 wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
85 {
86 m_inHelp = false;
87
88 if (beginHelp)
89 BeginContextHelp(win);
90 }
91
92 wxContextHelp::~wxContextHelp()
93 {
94 if (m_inHelp)
95 EndContextHelp();
96 }
97
98 // Not currently needed, but on some systems capture may not work as
99 // expected so we'll leave it here for now.
100 #ifdef __WXMOTIF__
101 static void wxPushOrPopEventHandlers(wxContextHelp* help, wxWindow* win, bool push)
102 {
103 if (push)
104 win->PushEventHandler(new wxContextHelpEvtHandler(help));
105 else
106 win->PopEventHandler(true);
107
108 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
109 while (node)
110 {
111 wxWindow* child = node->GetData();
112 wxPushOrPopEventHandlers(help, child, push);
113
114 node = node->GetNext();
115 }
116 }
117 #endif
118
119 // Begin 'context help mode'
120 bool wxContextHelp::BeginContextHelp(wxWindow* win)
121 {
122 if (!win)
123 win = wxTheApp->GetTopWindow();
124 if (!win)
125 return false;
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
135 m_status = false;
136
137 #ifdef __WXMOTIF__
138 wxPushOrPopEventHandlers(this, win, true);
139 #else
140 win->PushEventHandler(new wxContextHelpEvtHandler(this));
141 #endif
142
143 win->CaptureMouse();
144
145 EventLoop();
146
147 win->ReleaseMouse();
148
149 #ifdef __WXMOTIF__
150 wxPushOrPopEventHandlers(this, win, false);
151 #else
152 win->PopEventHandler(true);
153 #endif
154
155 win->SetCursor(oldCursor);
156
157 if (m_status)
158 {
159 wxPoint pt;
160 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
161
162 #if 0
163 if (winAtPtr)
164 {
165 printf("Picked %s (%d)\n", winAtPtr->GetName().c_str(),
166 winAtPtr->GetId());
167 }
168 #endif
169
170 if (winAtPtr)
171 DispatchEvent(winAtPtr, pt);
172 }
173
174 return true;
175 }
176
177 bool wxContextHelp::EndContextHelp()
178 {
179 m_inHelp = false;
180
181 return true;
182 }
183
184 bool wxContextHelp::EventLoop()
185 {
186 m_inHelp = true;
187
188 while ( m_inHelp )
189 {
190 if (wxTheApp->Pending())
191 {
192 wxTheApp->Dispatch();
193 }
194 else
195 {
196 wxTheApp->ProcessIdle();
197 }
198 }
199
200 return true;
201 }
202
203 bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
204 {
205 if (event.GetEventType() == wxEVT_LEFT_DOWN)
206 {
207 m_contextHelp->SetStatus(true);
208 m_contextHelp->EndContextHelp();
209 return true;
210 }
211
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 {
217 // May have already been set to true by a left-click
218 //m_contextHelp->SetStatus(false);
219 m_contextHelp->EndContextHelp();
220 return true;
221 }
222
223 if ((event.GetEventType() == wxEVT_PAINT) ||
224 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
225 {
226 event.Skip();
227 return false;
228 }
229
230 return true;
231 }
232
233 // Dispatch the help event to the relevant window
234 bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
235 {
236 wxCHECK_MSG( win, false, _T("win parameter can't be NULL") );
237
238 wxHelpEvent helpEvent(wxEVT_HELP, win->GetId(), pt,
239 wxHelpEvent::Origin_HelpButton);
240 helpEvent.SetEventObject(win);
241
242 return win->GetEventHandler()->ProcessEvent(helpEvent);
243 }
244
245 // ----------------------------------------------------------------------------
246 // wxContextHelpButton
247 // ----------------------------------------------------------------------------
248
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
255 #ifndef __WXPM__
256
257 static const char * csquery_xpm[] = {
258 "12 11 2 1",
259 " c None",
260 ". c #000000",
261 " ",
262 " .... ",
263 " .. .. ",
264 " .. .. ",
265 " .. ",
266 " .. ",
267 " .. ",
268 " ",
269 " .. ",
270 " .. ",
271 " "};
272
273 #endif
274
275 IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
276
277 BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
278 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
279 END_EVENT_TABLE()
280
281 wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
282 wxWindowID id,
283 const wxPoint& pos,
284 const wxSize& size,
285 long style)
286 #if defined(__WXPM__)
287 : wxBitmapButton(parent, id, wxBitmap(wxCSQUERY_BITMAP
288 ,wxBITMAP_TYPE_RESOURCE
289 ),
290 pos, size, style)
291 #else
292 : wxBitmapButton(parent, id, wxBitmap(csquery_xpm),
293 pos, size, style)
294 #endif
295 {
296 }
297
298 void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
299 {
300 wxContextHelp contextHelp(GetParent());
301 }
302
303 // ----------------------------------------------------------------------------
304 // wxHelpProvider
305 // ----------------------------------------------------------------------------
306
307 wxHelpProvider *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
312 void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
313 const wxString& WXUNUSED(text))
314 {
315 }
316
317 void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
318 const wxString& WXUNUSED(text))
319 {
320 }
321
322 // removes the association
323 void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
324 {
325 }
326
327 wxHelpProvider::~wxHelpProvider()
328 {
329 }
330
331 wxString 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
350 // ----------------------------------------------------------------------------
351 // wxSimpleHelpProvider
352 // ----------------------------------------------------------------------------
353
354 #define WINHASH_KEY(w) wxPtrToUInt(w)
355
356 wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
357 {
358 wxSimpleHelpProviderHashMap::iterator it = m_hashWindows.find(WINHASH_KEY(window));
359
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;
368 }
369
370 void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
371 {
372 m_hashWindows.erase(WINHASH_KEY(window));
373 m_hashWindows[WINHASH_KEY(window)] = text;
374 }
375
376 void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
377 {
378 wxSimpleHelpProviderHashMap::key_type key = (wxSimpleHelpProviderHashMap::key_type)id;
379 m_hashIds.erase(key);
380 m_hashIds[key] = text;
381 }
382
383 // removes the association
384 void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
385 {
386 m_hashWindows.erase(WINHASH_KEY(window));
387 }
388
389 bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
390 {
391 #if wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
392 const wxString text = GetHelpTextMaybeAtPoint(window);
393
394 if ( !text.empty() )
395 {
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 }
425
426 return true;
427 }
428 #else // !wxUSE_MS_HTML_HELP && !wxUSE_TIPWINDOW
429 wxUnusedVar(window);
430 #endif // wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
431
432 return false;
433 }
434
435 // ----------------------------------------------------------------------------
436 // wxHelpControllerHelpProvider
437 // ----------------------------------------------------------------------------
438
439 wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
440 {
441 m_helpController = hc;
442 }
443
444 bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
445 {
446 const wxString text = GetHelpTextMaybeAtPoint(window);
447
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;
461 }
462
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);
466 }
467
468 // Convenience function for turning context id into wxString
469 wxString wxContextId(int id)
470 {
471 return wxString::Format(_T("%d"), id);
472 }
473
474 // ----------------------------------------------------------------------------
475 // wxHelpProviderModule: module responsible for cleaning up help provider.
476 // ----------------------------------------------------------------------------
477
478 class wxHelpProviderModule : public wxModule
479 {
480 public:
481 bool OnInit();
482 void OnExit();
483
484 private:
485 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
486 };
487
488 IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
489
490 bool 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
496 return true;
497 }
498
499 void wxHelpProviderModule::OnExit()
500 {
501 if (wxHelpProvider::Get())
502 {
503 delete wxHelpProvider::Get();
504 wxHelpProvider::Set(NULL);
505 }
506 }
507
508 #endif // wxUSE_HELP