Remove obsolete VisualAge-related files.
[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 // Copyright: (c) 2000 Julian Smart, Vadim Zeitlin
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_HELP
27
28 #ifndef WX_PRECOMP
29 #include "wx/app.h"
30 #include "wx/module.h"
31 #endif
32
33 #include "wx/tipwin.h"
34 #include "wx/cshelp.h"
35
36 #if wxUSE_MS_HTML_HELP
37 #include "wx/msw/helpchm.h" // for ShowContextHelpPopup
38 #include "wx/utils.h" // for wxGetMousePosition()
39 #endif
40
41 // ----------------------------------------------------------------------------
42 // wxContextHelpEvtHandler private class
43 // ----------------------------------------------------------------------------
44
45 // This class exists in order to eat events until the left mouse button is
46 // pressed
47 class wxContextHelpEvtHandler: public wxEvtHandler
48 {
49 public:
50 wxContextHelpEvtHandler(wxContextHelp* contextHelp)
51 {
52 m_contextHelp = contextHelp;
53 }
54
55 virtual bool ProcessEvent(wxEvent& event);
56
57 //// Data
58 wxContextHelp* m_contextHelp;
59
60 wxDECLARE_NO_COPY_CLASS(wxContextHelpEvtHandler);
61 };
62
63 // ============================================================================
64 // implementation
65 // ============================================================================
66
67 // ----------------------------------------------------------------------------
68 // wxContextHelp
69 // ----------------------------------------------------------------------------
70
71 /*
72 * Invokes context-sensitive help
73 */
74
75
76 IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
77
78 wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
79 {
80 m_inHelp = false;
81
82 if (beginHelp)
83 BeginContextHelp(win);
84 }
85
86 wxContextHelp::~wxContextHelp()
87 {
88 if (m_inHelp)
89 EndContextHelp();
90 }
91
92 // Not currently needed, but on some systems capture may not work as
93 // expected so we'll leave it here for now.
94 #ifdef __WXMOTIF__
95 static void wxPushOrPopEventHandlers(wxContextHelp* help, wxWindow* win, bool push)
96 {
97 if (push)
98 win->PushEventHandler(new wxContextHelpEvtHandler(help));
99 else
100 win->PopEventHandler(true);
101
102 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
103 while (node)
104 {
105 wxWindow* child = node->GetData();
106 wxPushOrPopEventHandlers(help, child, push);
107
108 node = node->GetNext();
109 }
110 }
111 #endif
112
113 // Begin 'context help mode'
114 bool wxContextHelp::BeginContextHelp(wxWindow* win)
115 {
116 if (!win)
117 win = wxTheApp->GetTopWindow();
118 if (!win)
119 return false;
120
121 wxCursor cursor(wxCURSOR_QUESTION_ARROW);
122 wxCursor oldCursor = win->GetCursor();
123 win->SetCursor(cursor);
124
125 #ifdef __WXMAC__
126 wxSetCursor(cursor);
127 #endif
128
129 m_status = false;
130
131 #ifdef __WXMOTIF__
132 wxPushOrPopEventHandlers(this, win, true);
133 #else
134 win->PushEventHandler(new wxContextHelpEvtHandler(this));
135 #endif
136
137 win->CaptureMouse();
138
139 EventLoop();
140
141 win->ReleaseMouse();
142
143 #ifdef __WXMOTIF__
144 wxPushOrPopEventHandlers(this, win, false);
145 #else
146 win->PopEventHandler(true);
147 #endif
148
149 win->SetCursor(oldCursor);
150
151 #ifdef __WXMAC__
152 wxSetCursor(wxNullCursor);
153 #endif
154
155 if (m_status)
156 {
157 wxPoint pt;
158 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
159
160 #if 0
161 if (winAtPtr)
162 {
163 printf("Picked %s (%d)\n", winAtPtr->GetName().c_str(),
164 winAtPtr->GetId());
165 }
166 #endif
167
168 if (winAtPtr)
169 DispatchEvent(winAtPtr, pt);
170 }
171
172 return true;
173 }
174
175 bool wxContextHelp::EndContextHelp()
176 {
177 m_inHelp = false;
178
179 return true;
180 }
181
182 bool wxContextHelp::EventLoop()
183 {
184 m_inHelp = true;
185
186 while ( m_inHelp )
187 {
188 if (wxTheApp->Pending())
189 {
190 wxTheApp->Dispatch();
191 }
192 else
193 {
194 wxTheApp->ProcessIdle();
195 }
196 }
197
198 return true;
199 }
200
201 bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
202 {
203 if (event.GetEventType() == wxEVT_LEFT_DOWN)
204 {
205 m_contextHelp->SetStatus(true);
206 m_contextHelp->EndContextHelp();
207 return true;
208 }
209
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 {
215 // May have already been set to true by a left-click
216 //m_contextHelp->SetStatus(false);
217 m_contextHelp->EndContextHelp();
218 return true;
219 }
220
221 if ((event.GetEventType() == wxEVT_PAINT) ||
222 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
223 {
224 event.Skip();
225 return false;
226 }
227
228 return true;
229 }
230
231 // Dispatch the help event to the relevant window
232 bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
233 {
234 wxCHECK_MSG( win, false, wxT("win parameter can't be NULL") );
235
236 wxHelpEvent helpEvent(wxEVT_HELP, win->GetId(), pt,
237 wxHelpEvent::Origin_HelpButton);
238 helpEvent.SetEventObject(win);
239
240 return win->GetEventHandler()->ProcessEvent(helpEvent);
241 }
242
243 // ----------------------------------------------------------------------------
244 // wxContextHelpButton
245 // ----------------------------------------------------------------------------
246
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
253 #ifndef __WXPM__
254
255 static const char * csquery_xpm[] = {
256 "12 11 2 1",
257 " c None",
258 ". c #000000",
259 " ",
260 " .... ",
261 " .. .. ",
262 " .. .. ",
263 " .. ",
264 " .. ",
265 " .. ",
266 " ",
267 " .. ",
268 " .. ",
269 " "};
270
271 #endif
272
273 IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
274
275 BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
276 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
277 END_EVENT_TABLE()
278
279 wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
280 wxWindowID id,
281 const wxPoint& pos,
282 const wxSize& size,
283 long style)
284 #if defined(__WXPM__)
285 : wxBitmapButton(parent, id, wxBitmap(wxCSQUERY_BITMAP
286 ,wxBITMAP_TYPE_BMP_RESOURCE
287 ),
288 pos, size, style)
289 #else
290 : wxBitmapButton(parent, id, wxBitmap(csquery_xpm),
291 pos, size, style)
292 #endif
293 {
294 }
295
296 void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
297 {
298 wxContextHelp contextHelp(GetParent());
299 }
300
301 // ----------------------------------------------------------------------------
302 // wxHelpProvider
303 // ----------------------------------------------------------------------------
304
305 wxHelpProvider *wxHelpProvider::ms_helpProvider = NULL;
306
307 // trivial implementation of some methods which we don't want to make pure
308 // virtual for convenience
309
310 void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
311 const wxString& WXUNUSED(text))
312 {
313 }
314
315 void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
316 const wxString& WXUNUSED(text))
317 {
318 }
319
320 // removes the association
321 void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
322 {
323 }
324
325 wxHelpProvider::~wxHelpProvider()
326 {
327 }
328
329 wxString wxHelpProvider::GetHelpTextMaybeAtPoint(wxWindowBase *window)
330 {
331 if ( m_helptextAtPoint != wxDefaultPosition ||
332 m_helptextOrigin != wxHelpEvent::Origin_Unknown )
333 {
334 wxCHECK_MSG( window, wxEmptyString, wxT("window must not be NULL") );
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
348 // ----------------------------------------------------------------------------
349 // wxSimpleHelpProvider
350 // ----------------------------------------------------------------------------
351
352 #define WINHASH_KEY(w) wxPtrToUInt(w)
353
354 wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
355 {
356 wxSimpleHelpProviderHashMap::iterator it = m_hashWindows.find(WINHASH_KEY(window));
357
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;
366 }
367
368 void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
369 {
370 m_hashWindows.erase(WINHASH_KEY(window));
371 m_hashWindows[WINHASH_KEY(window)] = text;
372 }
373
374 void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
375 {
376 wxSimpleHelpProviderHashMap::key_type key = (wxSimpleHelpProviderHashMap::key_type)id;
377 m_hashIds.erase(key);
378 m_hashIds[key] = text;
379 }
380
381 // removes the association
382 void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
383 {
384 m_hashWindows.erase(WINHASH_KEY(window));
385 }
386
387 bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
388 {
389 #if wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
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
395 const wxString text = GetHelpTextMaybeAtPoint(window);
396
397 if ( !text.empty() )
398 {
399 // use the native help popup style if it's available
400 #if wxUSE_MS_HTML_HELP
401 if ( !wxCHMHelpController::ShowContextHelpPopup
402 (
403 text,
404 posTooltip,
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 }
428
429 return true;
430 }
431 #else // !wxUSE_MS_HTML_HELP && !wxUSE_TIPWINDOW
432 wxUnusedVar(window);
433 #endif // wxUSE_MS_HTML_HELP || wxUSE_TIPWINDOW
434
435 return false;
436 }
437
438 // ----------------------------------------------------------------------------
439 // wxHelpControllerHelpProvider
440 // ----------------------------------------------------------------------------
441
442 wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
443 {
444 m_helpController = hc;
445 }
446
447 bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
448 {
449 const wxString text = GetHelpTextMaybeAtPoint(window);
450
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;
464 }
465
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);
469 }
470
471 // Convenience function for turning context id into wxString
472 wxString wxContextId(int id)
473 {
474 return wxString::Format(wxT("%d"), id);
475 }
476
477 // ----------------------------------------------------------------------------
478 // wxHelpProviderModule: module responsible for cleaning up help provider.
479 // ----------------------------------------------------------------------------
480
481 class wxHelpProviderModule : public wxModule
482 {
483 public:
484 bool OnInit();
485 void OnExit();
486
487 private:
488 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
489 };
490
491 IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
492
493 bool 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
499 return true;
500 }
501
502 void wxHelpProviderModule::OnExit()
503 {
504 if (wxHelpProvider::Get())
505 {
506 delete wxHelpProvider::Get();
507 wxHelpProvider::Set(NULL);
508 }
509 }
510
511 #endif // wxUSE_HELP