Dislaying popup tip windows seems to work right now.
[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 #ifdef __GNUG__
17 #pragma implementation "cshelp.h"
18 #endif
19
20 // ----------------------------------------------------------------------------
21 // headers
22 // ----------------------------------------------------------------------------
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_HELP
32
33 #ifndef WX_PRECOMP
34 #endif
35
36 #include "wx/tipwin.h"
37 #include "wx/app.h"
38 #include "wx/module.h"
39 #include "wx/cshelp.h"
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
61 // ============================================================================
62 // implementation
63 // ============================================================================
64
65 // ----------------------------------------------------------------------------
66 // wxContextHelp
67 // ----------------------------------------------------------------------------
68
69 /*
70 * Invokes context-sensitive help
71 */
72
73
74 IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
75
76 wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
77 {
78 m_inHelp = FALSE;
79
80 if (beginHelp)
81 BeginContextHelp(win);
82 }
83
84 wxContextHelp::~wxContextHelp()
85 {
86 if (m_inHelp)
87 EndContextHelp();
88 }
89
90 // Not currently needed, but on some systems capture may not work as
91 // expected so we'll leave it here for now.
92 #if 0
93 static void wxPushOrPopEventHandlers(wxContextHelp* help, wxWindow* win, bool push)
94 {
95 if (push)
96 win->PushEventHandler(new wxContextHelpEvtHandler(help));
97 else
98 win->PopEventHandler();
99
100 wxNode* node = win->GetChildren().First();
101 while (node)
102 {
103 wxWindow* child = (wxWindow*) node->Data();
104 wxPushOrPopEventHandlers(help, child, push);
105
106 node = node->Next();
107 }
108 }
109 #endif
110
111 // Begin 'context help mode'
112 bool wxContextHelp::BeginContextHelp(wxWindow* win)
113 {
114 if (!win)
115 win = wxTheApp->GetTopWindow();
116 if (!win)
117 return FALSE;
118
119 wxCursor cursor(wxCURSOR_QUESTION_ARROW);
120 wxCursor oldCursor = win->GetCursor();
121 win->SetCursor(cursor);
122
123 #ifdef __WXMSW__
124 // wxSetCursor(cursor);
125 #endif
126
127 m_status = FALSE;
128
129 win->PushEventHandler(new wxContextHelpEvtHandler(this));
130 //wxPushOrPopEventHandlers(this, win, TRUE);
131
132 win->CaptureMouse();
133
134 EventLoop();
135
136 win->ReleaseMouse();
137
138 win->PopEventHandler(TRUE);
139 //wxPushOrPopEventHandlers(this, win, FALSE);
140
141 win->SetCursor(oldCursor);
142
143 if (m_status)
144 {
145 wxPoint pt;
146 wxWindow* winAtPtr = wxFindWindowAtPointer(pt);
147 /*
148 if (winAtPtr)
149 {
150 wxString msg;
151 msg.Printf("Picked %s (%d)", (const char*) winAtPtr->GetName(), winAtPtr->GetId());
152 cout << msg << '\n';
153 }
154 */
155
156 if (winAtPtr)
157 DispatchEvent(winAtPtr, pt);
158 }
159
160 return TRUE;
161 }
162
163 bool wxContextHelp::EndContextHelp()
164 {
165 m_inHelp = FALSE;
166
167 return TRUE;
168 }
169
170 bool wxContextHelp::EventLoop()
171 {
172 m_inHelp = TRUE;
173
174 while ( m_inHelp )
175 {
176 if (wxTheApp->Pending())
177 {
178 wxTheApp->Dispatch();
179 }
180 else
181 {
182 wxTheApp->ProcessIdle();
183 }
184 }
185
186 return TRUE;
187 }
188
189 bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
190 {
191 if (event.GetEventType() == wxEVT_LEFT_DOWN)
192 {
193 m_contextHelp->SetStatus(TRUE);
194 m_contextHelp->EndContextHelp();
195 return TRUE;
196 }
197
198 if ((event.GetEventType() == wxEVT_CHAR) ||
199 (event.GetEventType() == wxEVT_KEY_DOWN) ||
200 (event.GetEventType() == wxEVT_ACTIVATE) ||
201 (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
202 {
203 // May have already been set to TRUE by a left-click
204 //m_contextHelp->SetStatus(FALSE);
205 m_contextHelp->EndContextHelp();
206 return TRUE;
207 }
208
209 if ((event.GetEventType() == wxEVT_PAINT) ||
210 (event.GetEventType() == wxEVT_ERASE_BACKGROUND))
211 {
212 event.Skip();
213 return FALSE;
214 }
215
216 return TRUE;
217 }
218
219 // Dispatch the help event to the relevant window
220 bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
221 {
222 wxWindow* subjectOfHelp = win;
223 bool eventProcessed = FALSE;
224 while (subjectOfHelp && !eventProcessed)
225 {
226 wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), pt) ;
227 helpEvent.SetEventObject(this);
228
229 eventProcessed = win->GetEventHandler()->ProcessEvent(helpEvent);
230
231 // Go up the window hierarchy until the event is handled (or not).
232 // I.e. keep submitting ancestor windows until one is recognised
233 // by the app code that processes the ids and displays help.
234 subjectOfHelp = subjectOfHelp->GetParent();
235 }
236 return eventProcessed;
237 }
238
239 // ----------------------------------------------------------------------------
240 // wxContextHelpButton
241 // ----------------------------------------------------------------------------
242
243 /*
244 * wxContextHelpButton
245 * You can add this to your dialogs (especially on non-Windows platforms)
246 * to put the application into context help mode.
247 */
248
249 #if !defined(__WXMSW__)
250 static const char * csquery_xpm[] = {
251 "12 11 2 1",
252 " c None",
253 ". c #000000",
254 " ",
255 " .... ",
256 " .. .. ",
257 " .. .. ",
258 " .. ",
259 " .. ",
260 " .. ",
261 " ",
262 " .. ",
263 " .. ",
264 " "};
265 #endif
266
267 IMPLEMENT_CLASS(wxContextHelpButton, wxBitmapButton)
268
269 BEGIN_EVENT_TABLE(wxContextHelpButton, wxBitmapButton)
270 EVT_BUTTON(wxID_CONTEXT_HELP, wxContextHelpButton::OnContextHelp)
271 END_EVENT_TABLE()
272
273 wxContextHelpButton::wxContextHelpButton(wxWindow* parent,
274 wxWindowID id,
275 const wxPoint& pos,
276 const wxSize& size,
277 long style)
278 : wxBitmapButton(parent, id, wxBITMAP(csquery),
279 pos, size, style)
280 {
281 }
282
283 void wxContextHelpButton::OnContextHelp(wxCommandEvent& WXUNUSED(event))
284 {
285 wxContextHelp contextHelp(GetParent());
286 }
287
288 // ----------------------------------------------------------------------------
289 // wxHelpProvider
290 // ----------------------------------------------------------------------------
291
292 wxHelpProvider *wxHelpProvider::ms_helpProvider = (wxHelpProvider *)NULL;
293
294 // trivial implementation of some methods which we don't want to make pure
295 // virtual for convenience
296
297 void wxHelpProvider::AddHelp(wxWindowBase * WXUNUSED(window),
298 const wxString& WXUNUSED(text))
299 {
300 }
301
302 void wxHelpProvider::AddHelp(wxWindowID WXUNUSED(id),
303 const wxString& WXUNUSED(text))
304 {
305 }
306
307 // removes the association
308 void wxHelpProvider::RemoveHelp(wxWindowBase* WXUNUSED(window))
309 {
310 }
311
312 wxHelpProvider::~wxHelpProvider()
313 {
314 }
315
316 // ----------------------------------------------------------------------------
317 // wxSimpleHelpProvider
318 // ----------------------------------------------------------------------------
319
320 wxString wxSimpleHelpProvider::GetHelp(const wxWindowBase *window)
321 {
322 bool wasFound;
323 wxString text = m_hashWindows.Get((long)window, &wasFound);
324 if ( !wasFound )
325 text = m_hashIds.Get(window->GetId());
326
327 return text;
328 }
329
330 void wxSimpleHelpProvider::AddHelp(wxWindowBase *window, const wxString& text)
331 {
332 m_hashWindows.Put((long)window, text);
333 }
334
335 void wxSimpleHelpProvider::AddHelp(wxWindowID id, const wxString& text)
336 {
337 m_hashIds.Put(id, text);
338 }
339
340 // removes the association
341 void wxSimpleHelpProvider::RemoveHelp(wxWindowBase* window)
342 {
343 m_hashWindows.Delete((long)window);
344 }
345
346 bool wxSimpleHelpProvider::ShowHelp(wxWindowBase *window)
347 {
348 #if wxUSE_TIPWINDOW
349 static wxTipWindow* s_tipWindow = NULL;
350
351 if (s_tipWindow)
352 {
353 // Prevent s_tipWindow being nulled in OnIdle,
354 // thereby removing the chance for the window to be closed by ShowHelp
355 s_tipWindow->SetTipWindowPtr(NULL);
356 s_tipWindow->Close();
357 }
358 s_tipWindow = NULL;
359
360 wxString text = GetHelp(window);
361 if ( !text.empty() )
362 {
363 s_tipWindow = new wxTipWindow((wxWindow *)window, text, 100, & s_tipWindow);
364
365 return TRUE;
366 }
367 #endif // wxUSE_TIPWINDOW
368
369 return FALSE;
370 }
371
372 // ----------------------------------------------------------------------------
373 // wxHelpControllerHelpProvider
374 // ----------------------------------------------------------------------------
375
376 wxHelpControllerHelpProvider::wxHelpControllerHelpProvider(wxHelpControllerBase* hc)
377 {
378 m_helpController = hc;
379 }
380
381 bool wxHelpControllerHelpProvider::ShowHelp(wxWindowBase *window)
382 {
383 wxString text = GetHelp(window);
384 if ( !text.empty() )
385 {
386 if (m_helpController)
387 {
388 if (text.IsNumber())
389 return m_helpController->DisplayContextPopup(wxAtoi(text));
390
391 // If the help controller is capable of popping up the text...
392 else if (m_helpController->DisplayTextPopup(text, wxGetMousePosition()))
393 {
394 return TRUE;
395 }
396 else
397 // ...else use the default method.
398 return wxSimpleHelpProvider::ShowHelp(window);
399 }
400 else
401 return wxSimpleHelpProvider::ShowHelp(window);
402
403 }
404
405 return FALSE;
406 }
407
408 // Convenience function for turning context id into wxString
409 wxString wxContextId(int id)
410 {
411 return wxString(IntToString(id));
412 }
413
414 // ----------------------------------------------------------------------------
415 // wxHelpProviderModule: module responsible for cleaning up help provider.
416 // ----------------------------------------------------------------------------
417
418 class wxHelpProviderModule : public wxModule
419 {
420 public:
421 bool OnInit();
422 void OnExit();
423
424 private:
425 DECLARE_DYNAMIC_CLASS(wxHelpProviderModule)
426 };
427
428 IMPLEMENT_DYNAMIC_CLASS(wxHelpProviderModule, wxModule)
429
430 bool wxHelpProviderModule::OnInit()
431 {
432 // Probably we don't want to do anything by default,
433 // since it could pull in extra code
434 // wxHelpProvider::Set(new wxSimpleHelpProvider);
435
436 return TRUE;
437 }
438
439 void wxHelpProviderModule::OnExit()
440 {
441 if (wxHelpProvider::Get())
442 {
443 delete wxHelpProvider::Get();
444 wxHelpProvider::Set(NULL);
445 }
446 }
447
448 #endif // wxUSE_HELP