Implemented wxHelpContext for wxGTK.
[wxWidgets.git] / src / common / helpbase.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: helpbase.cpp
3 // Purpose: Help system base classes
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "helpbase.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/defs.h"
25 #endif
26
27 #include "wx/helpbase.h"
28 #include "wx/app.h"
29
30 #ifdef __WXMSW__
31 #include "wx/msw/private.h"
32 #endif
33
34 #ifdef __WXGTK__
35 #include <gtk/gtk.h>
36 #include <gtk/gtkprivate.h>
37 #include "wx/gtk/win_gtk.h"
38 #include "wx/msgdlg.h"
39 #endif
40
41 #if wxUSE_HELP
42
43 IMPLEMENT_CLASS(wxHelpControllerBase, wxObject)
44
45 /*
46 * Invokes context-sensitive help
47 */
48
49 #ifdef __WXGTK__
50 // This class exists in order to eat events until the left mouse
51 // button is pressed
52 class wxContextHelpEvtHandler: public wxEvtHandler
53 {
54 public:
55 wxContextHelpEvtHandler(wxContextHelp* contextHelp)
56 {
57 m_contextHelp = contextHelp;
58 }
59
60 virtual bool ProcessEvent(wxEvent& event);
61
62 //// Data
63 wxContextHelp* m_contextHelp;
64 };
65 #endif
66
67 IMPLEMENT_DYNAMIC_CLASS(wxContextHelp, wxObject)
68
69 wxContextHelp::wxContextHelp(wxWindow* win, bool beginHelp)
70 {
71 m_inHelp = FALSE;
72
73 if (beginHelp)
74 BeginContextHelp(win);
75 }
76
77 wxContextHelp::~wxContextHelp()
78 {
79 if (m_inHelp)
80 EndContextHelp();
81 }
82
83 #ifdef __WXGTK__
84 wxWindow* wxFindWindowForGdkWindow(wxWindow* win, GdkWindow* gdkWindow)
85 {
86 GdkWindow* thisGdkWindow1 = 0;
87 GdkWindow* thisGdkWindow2 = 0;
88
89 if (win->m_wxwindow)
90 thisGdkWindow1 = GTK_PIZZA(win->m_wxwindow)->bin_window;
91
92 thisGdkWindow2 = win->m_widget->window;
93
94 if (gdkWindow == thisGdkWindow1 || gdkWindow == thisGdkWindow2)
95 return win;
96
97 wxNode* node = win->GetChildren().First();
98 while (node)
99 {
100 wxWindow* child = (wxWindow*) node->Data();
101 wxWindow* found = wxFindWindowForGdkWindow(child, gdkWindow);
102 if (found)
103 return found;
104
105 node = node->Next();
106 }
107 return NULL;
108 }
109 #endif
110
111 bool wxContextHelp::BeginContextHelp(wxWindow* win)
112 {
113 if (!win)
114 win = wxTheApp->GetTopWindow();
115 if (!win)
116 return FALSE;
117 #ifdef __WXMSW__
118 wxCursor cursor(wxCURSOR_QUESTION_ARROW);
119 wxSetCursor(cursor);
120
121 win->CaptureMouse();
122
123 EventLoop(cursor, win);
124
125 win->ReleaseMouse();
126 #endif
127
128 #ifdef __WXGTK__
129 m_status = FALSE;
130 GdkCursor* query_cursor = gdk_cursor_new (GDK_QUESTION_ARROW);
131
132 GdkWindow* gdkWindow = 0;
133 GtkWidget* gtkWidget = 0;
134
135 if (win->m_wxwindow)
136 {
137 gtkWidget = win->m_wxwindow;
138 gdkWindow = GTK_PIZZA(win->m_wxwindow)->bin_window;
139 }
140 else
141 {
142 gtkWidget = win->m_widget;
143 gdkWindow = win->m_widget->window;
144 }
145
146 gint failure = gdk_pointer_grab (gdkWindow,
147 FALSE,
148 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
149 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK,
150 NULL,
151 query_cursor,
152 GDK_CURRENT_TIME);
153 if (failure)
154 {
155 gdk_cursor_destroy (query_cursor);
156 query_cursor = NULL;
157 }
158 gdk_keyboard_grab (gdkWindow, FALSE, GDK_CURRENT_TIME);
159 gtk_grab_add (gtkWidget);
160
161 win->PushEventHandler(new wxContextHelpEvtHandler(this));
162
163 // wxLogDebug("Entering loop.");
164
165 EventLoop(wxNullCursor, win);
166
167 // wxLogDebug("Exiting loop.");
168
169 win->PopEventHandler(TRUE);
170
171 gtk_grab_remove (gtkWidget);
172 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
173 if (query_cursor)
174 {
175 gdk_pointer_ungrab (GDK_CURRENT_TIME);
176 gdk_cursor_destroy (query_cursor);
177 query_cursor = NULL;
178 }
179
180 if (m_status)
181 {
182 //wxMessageBox("Left-clicked");
183 //wxPoint screenPt = win->ClientToScreen(m_mousePos);
184 int x, y;
185 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
186 if (windowAtPtr)
187 {
188 wxWindow* wxWinAtPtr = wxFindWindowForGdkWindow(win, windowAtPtr);
189 if (wxWinAtPtr)
190 {
191 DispatchEvent(wxWinAtPtr, wxPoint(x, y));
192 }
193 }
194 }
195 else
196 {
197 //wxMessageBox("Cancelled");
198 }
199 #endif
200
201 return TRUE;
202 }
203
204 bool wxContextHelp::EndContextHelp()
205 {
206 m_inHelp = FALSE;
207
208 return TRUE;
209 }
210
211 bool wxContextHelp::EventLoop(const wxCursor& cursor, wxWindow* win)
212 {
213 #ifdef __WXMSW__
214 m_inHelp = TRUE;
215 while ( m_inHelp )
216 {
217 MSG msg;
218 if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
219 {
220 if (!ProcessHelpMessage((WXMSG*) & msg, cursor, win))
221 {
222 m_inHelp = FALSE;
223 }
224 }
225 else
226 {
227 wxTheApp->ProcessIdle();
228 }
229 }
230 return TRUE;
231 #elif defined(__WXGTK__)
232 m_inHelp = TRUE;
233 while ( m_inHelp )
234 {
235 if (wxTheApp->Pending())
236 {
237 wxTheApp->Dispatch();
238 }
239 else
240 {
241 wxTheApp->ProcessIdle();
242 }
243 }
244 return TRUE;
245 #else
246 return FALSE;
247 #endif
248 }
249
250 // PROBLEM: If you click on the panel or other descendant of the
251 // given window, then it doesn't go to this handler, even though
252 // there's a grab.
253 bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
254 {
255 //wxLogDebug("Got event");
256
257 if (event.GetEventType() == wxEVT_LEFT_DOWN)
258 {
259 //wxLogDebug("Mouse event");
260 wxMouseEvent& mouseEvent = (wxMouseEvent&) event;
261 m_contextHelp->SetStatus(TRUE, mouseEvent.GetPosition());
262 m_contextHelp->EndContextHelp();
263 }
264 // Don't know why these aren't being caught
265 else if (event.GetEventType() == wxEVT_CHAR || event.GetEventType() == wxEVT_KEY_DOWN)
266 {
267 //wxKeyEvent& keyEvent = (wxKeyEvent&) event;
268 if (TRUE) // keyEvent.GetKeyCode() == WXK_ESCAPE)
269 {
270 m_contextHelp->SetStatus(FALSE, wxPoint(0, 0));
271 m_contextHelp->EndContextHelp();
272 }
273 }
274
275 return TRUE;
276 }
277
278 #ifdef __WXMSW__
279 bool wxContextHelp::ProcessHelpMessage(WXMSG* wxmsg, const wxCursor& cursor, wxWindow* winInQuestion)
280 {
281 MSG& msg = * (MSG*) wxmsg;
282
283 if (msg.message == WM_KEYDOWN || msg.wParam == VK_ESCAPE)
284 {
285 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
286 return FALSE;
287 }
288
289 if (msg.message == WM_CAPTURECHANGED)
290 {
291 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
292 return FALSE;
293 }
294
295 if (msg.message == WM_ACTIVATE)
296 {
297 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
298 return FALSE;
299 }
300
301 if ((msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST))
302 // || (msg.message >= WM_NCMOUSEFIRST && msg.message <= WM_NCMOUSELAST))
303 {
304 wxSetCursor(cursor);
305
306 HWND hWndHit = ::WindowFromPoint(msg.pt);
307
308 wxWindow* win = wxFindWinFromHandle((WXHWND) hWndHit) ;
309 HWND hWnd = hWndHit;
310
311 // Try to find a window with a wxWindow associated with it
312 while (!win && (hWnd != 0))
313 {
314 hWnd = ::GetParent(hWnd);
315 win = wxFindWinFromHandle((WXHWND) hWnd) ;
316 }
317
318 if (win)
319 {
320 // It's a wxWindows window
321 if (msg.message != WM_LBUTTONDOWN)
322 {
323 // Hit one of our owned windows -- eat the message.
324 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
325 return TRUE;
326 }
327 int iHit = (int)::SendMessage(hWndHit, WM_NCHITTEST, 0,
328 MAKELONG(msg.pt.x, msg.pt.y));
329 if (iHit == HTMENU || iHit == HTSYSMENU)
330 {
331 // Eat this message, send the event and return
332 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
333 DispatchEvent(win, wxPoint(msg.pt.x, msg.pt.y));
334 return FALSE;
335 }
336 else if (iHit == HTCLIENT)
337 {
338 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
339 DispatchEvent(win, wxPoint(msg.pt.x, msg.pt.y));
340 return FALSE;
341 }
342 else
343 {
344 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
345 return FALSE;
346 }
347 }
348 else
349 {
350 // Someone else's message
351 if (PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE))
352 {
353 ::TranslateMessage(&msg);
354 ::DispatchMessage(&msg);
355 }
356 return TRUE;
357 }
358 }
359 else
360 {
361 // allow all other messages to go through (capture still set)
362 if (PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE))
363 DispatchMessage(&msg);
364 return TRUE;
365
366 }
367 return TRUE;
368 }
369 #endif
370
371 // Dispatch the help event to the relevant window
372 bool wxContextHelp::DispatchEvent(wxWindow* win, const wxPoint& pt)
373 {
374 wxWindow* subjectOfHelp = win;
375 bool eventProcessed = FALSE;
376 while (subjectOfHelp && !eventProcessed)
377 {
378 wxHelpEvent helpEvent(wxEVT_HELP, subjectOfHelp->GetId(), pt) ;
379 helpEvent.SetEventObject(this);
380 eventProcessed = win->GetEventHandler()->ProcessEvent(helpEvent);
381
382 // Go up the window hierarchy until the event is handled (or not).
383 // I.e. keep submitting ancestor windows until one is recognised
384 // by the app code that processes the ids and displays help.
385 subjectOfHelp = subjectOfHelp->GetParent();
386 }
387 return eventProcessed;
388 }
389
390
391 #endif // wxUSE_HELP