]> git.saurik.com Git - wxWidgets.git/blob - src/msw/evtloop.cpp
48dfc765232c5f3ce1ae8708fb10867c91c4644a
[wxWidgets.git] / src / msw / evtloop.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: msw/evtloop.cpp
3 // Purpose: implements wxEventLoop for MSW
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 01.06.01
7 // RCS-ID: $Id$
8 // Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // License: 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 #ifndef WX_PRECOMP
28 #if wxUSE_GUI
29 #include "wx/window.h"
30 #endif
31 #include "wx/app.h"
32 #endif //WX_PRECOMP
33
34 #include "wx/evtloop.h"
35
36
37 #include "wx/except.h"
38 #include "wx/ptr_scpd.h"
39
40 #include "wx/msw/private.h"
41
42 #if wxUSE_GUI
43 #include "wx/tooltip.h"
44 #if wxUSE_THREADS
45 #include "wx/thread.h"
46
47 // define the list of MSG strutures
48 WX_DECLARE_LIST(MSG, wxMsgList);
49
50 #include "wx/listimpl.cpp"
51
52 WX_DEFINE_LIST(wxMsgList)
53 #endif // wxUSE_THREADS
54 #endif //wxUSE_GUI
55
56 #if wxUSE_BASE
57
58 // ============================================================================
59 // wxMSWEventLoopBase implementation
60 // ============================================================================
61
62 // ----------------------------------------------------------------------------
63 // ctor/dtor
64 // ----------------------------------------------------------------------------
65
66 wxMSWEventLoopBase::wxMSWEventLoopBase()
67 {
68 m_shouldExit = false;
69 m_exitcode = 0;
70 }
71
72 // ----------------------------------------------------------------------------
73 // wxEventLoop message processing dispatching
74 // ----------------------------------------------------------------------------
75
76 bool wxMSWEventLoopBase::Pending() const
77 {
78 MSG msg;
79 return ::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE) != 0;
80 }
81
82 bool wxMSWEventLoopBase::GetNextMessage(WXMSG* msg)
83 {
84 wxCHECK_MSG( IsRunning(), false, _T("can't get messages if not running") );
85
86 const BOOL rc = ::GetMessage(msg, NULL, 0, 0);
87
88 if ( rc == 0 )
89 {
90 // got WM_QUIT
91 return false;
92 }
93
94 if ( rc == -1 )
95 {
96 // should never happen, but let's test for it nevertheless
97 wxLogLastError(wxT("GetMessage"));
98
99 // still break from the loop
100 return false;
101 }
102
103 return true;
104 }
105
106 #endif // wxUSE_BASE
107
108 #if wxUSE_GUI
109
110 // ============================================================================
111 // GUI wxEventLoop implementation
112 // ============================================================================
113
114 wxWindowMSW *wxGUIEventLoop::ms_winCritical = NULL;
115
116 bool wxGUIEventLoop::IsChildOfCriticalWindow(wxWindowMSW *win)
117 {
118 while ( win )
119 {
120 if ( win == ms_winCritical )
121 return true;
122
123 win = win->GetParent();
124 }
125
126 return false;
127 }
128
129 bool wxGUIEventLoop::PreProcessMessage(WXMSG *msg)
130 {
131 HWND hwnd = msg->hwnd;
132 wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
133 wxWindow *wnd;
134
135 // this might happen if we're in a modeless dialog, or if a wx control has
136 // children which themselves were not created by wx (i.e. wxActiveX control children)
137 if ( !wndThis )
138 {
139 while ( hwnd && (::GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD ))
140 {
141 hwnd = ::GetParent(hwnd);
142
143 // If the control has a wx parent, break and give the parent a chance
144 // to process the window message
145 wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
146 if (wndThis != NULL)
147 break;
148 }
149
150 if ( !wndThis )
151 {
152 // this may happen if the event occurred in a standard modeless dialog (the
153 // only example of which I know of is the find/replace dialog) - then call
154 // IsDialogMessage() to make TAB navigation in it work
155
156 // NOTE: IsDialogMessage() just eats all the messages (i.e. returns true for
157 // them) if we call it for the control itself
158 return hwnd && ::IsDialogMessage(hwnd, msg) != 0;
159 }
160 }
161
162 if ( !AllowProcessing(wndThis) )
163 {
164 // not a child of critical window, so we eat the event but take care to
165 // stop an endless stream of WM_PAINTs which would have resulted if we
166 // didn't validate the invalidated part of the window
167 if ( msg->message == WM_PAINT )
168 ::ValidateRect(hwnd, NULL);
169
170 return true;
171 }
172
173 #if wxUSE_TOOLTIPS
174 // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to
175 // popup the tooltip bubbles
176 if ( msg->message == WM_MOUSEMOVE )
177 {
178 // we should do it if one of window children has an associated tooltip
179 // (and not just if the window has a tooltip itself)
180 if ( wndThis->HasToolTips() )
181 wxToolTip::RelayEvent((WXMSG *)msg);
182 }
183 #endif // wxUSE_TOOLTIPS
184
185 // allow the window to prevent certain messages from being
186 // translated/processed (this is currently used by wxTextCtrl to always
187 // grab Ctrl-C/V/X, even if they are also accelerators in some parent)
188 if ( !wndThis->MSWShouldPreProcessMessage((WXMSG *)msg) )
189 {
190 return false;
191 }
192
193 // try translations first: the accelerators override everything
194 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
195 {
196 if ( wnd->MSWTranslateMessage((WXMSG *)msg))
197 return true;
198
199 // stop at first top level window, i.e. don't try to process the key
200 // strokes originating in a dialog using the accelerators of the parent
201 // frame - this doesn't make much sense
202 if ( wnd->IsTopLevel() )
203 break;
204 }
205
206 // now try the other hooks (kbd navigation is handled here)
207 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
208 {
209 if ( wnd->MSWProcessMessage((WXMSG *)msg) )
210 return true;
211
212 // also stop at first top level window here, just as above because
213 // if we don't do this, pressing ESC on a modal dialog shown as child
214 // of a modal dialog with wxID_CANCEL will cause the parent dialog to
215 // be closed, for example
216 if ( wnd->IsTopLevel() )
217 break;
218 }
219
220 // no special preprocessing for this message, dispatch it normally
221 return false;
222 }
223
224 void wxGUIEventLoop::ProcessMessage(WXMSG *msg)
225 {
226 // give us the chance to preprocess the message first
227 if ( !PreProcessMessage(msg) )
228 {
229 // if it wasn't done, dispatch it to the corresponding window
230 ::TranslateMessage(msg);
231 ::DispatchMessage(msg);
232 }
233 }
234
235 bool wxGUIEventLoop::Dispatch()
236 {
237 MSG msg;
238 if ( !GetNextMessage(&msg) )
239 return false;
240
241 #if wxUSE_THREADS
242 wxASSERT_MSG( wxThread::IsMain(),
243 wxT("only the main thread can process Windows messages") );
244
245 static bool s_hadGuiLock = true;
246 static wxMsgList s_aSavedMessages;
247
248 // if a secondary thread owning the mutex is doing GUI calls, save all
249 // messages for later processing - we can't process them right now because
250 // it will lead to recursive library calls (and we're not reentrant)
251 if ( !wxGuiOwnedByMainThread() )
252 {
253 s_hadGuiLock = false;
254
255 // leave out WM_COMMAND messages: too dangerous, sometimes
256 // the message will be processed twice
257 if ( !wxIsWaitingForThread() || msg.message != WM_COMMAND )
258 {
259 MSG* pMsg = new MSG(msg);
260 s_aSavedMessages.Append(pMsg);
261 }
262
263 return true;
264 }
265 else
266 {
267 // have we just regained the GUI lock? if so, post all of the saved
268 // messages
269 //
270 // FIXME of course, it's not _exactly_ the same as processing the
271 // messages normally - expect some things to break...
272 if ( !s_hadGuiLock )
273 {
274 s_hadGuiLock = true;
275
276 wxMsgList::compatibility_iterator node = s_aSavedMessages.GetFirst();
277 while (node)
278 {
279 MSG* pMsg = node->GetData();
280 s_aSavedMessages.Erase(node);
281
282 ProcessMessage(pMsg);
283 delete pMsg;
284
285 node = s_aSavedMessages.GetFirst();
286 }
287 }
288 }
289 #endif // wxUSE_THREADS
290
291 ProcessMessage(&msg);
292
293 return true;
294 }
295
296 void wxGUIEventLoop::OnNextIteration()
297 {
298 #if wxUSE_THREADS
299 wxMutexGuiLeaveOrEnter();
300 #endif // wxUSE_THREADS
301 }
302
303 void wxGUIEventLoop::WakeUp()
304 {
305 ::PostMessage(NULL, WM_NULL, 0, 0);
306 }
307
308 #else // !wxUSE_GUI
309
310 #if wxUSE_CONSOLE_EVENTLOOP
311
312 void wxConsoleEventLoop::OnNextIteration()
313 {
314 if ( wxTheApp )
315 wxTheApp->ProcessPendingEvents();
316 }
317
318 void wxConsoleEventLoop::WakeUp()
319 {
320 #if wxUSE_THREADS
321 wxWakeUpMainThread();
322 #endif
323 }
324
325 bool wxConsoleEventLoop::Dispatch()
326 {
327 MSG msg;
328 if ( !GetNextMessage(&msg) )
329 return false;
330
331 if ( msg.message == WM_TIMER )
332 {
333 TIMERPROC proc = (TIMERPROC)msg.lParam;
334 if ( proc )
335 (*proc)(NULL, 0, msg.wParam, 0);
336 }
337 else
338 {
339 wxLogDebug(_T("Ignoring unexpected message %d"), msg.message);
340 }
341
342 return !m_shouldExit;
343 }
344
345 #endif // wxUSE_CONSOLE_EVENTLOOP
346
347 #endif //wxUSE_GUI