]>
Commit | Line | Data |
---|---|---|
1649d288 VZ |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Name: tests/events/propagation.cpp | |
3 | // Purpose: Test events propagation | |
4 | // Author: Vadim Zeitlin | |
5 | // Created: 2009-01-16 | |
6 | // RCS-ID: $Id$ | |
7 | // Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org> | |
8 | /////////////////////////////////////////////////////////////////////////////// | |
9 | ||
10 | // ---------------------------------------------------------------------------- | |
11 | // headers | |
12 | // ---------------------------------------------------------------------------- | |
13 | ||
14 | #include "testprec.h" | |
15 | ||
16 | #ifdef __BORLANDC__ | |
17 | #pragma hdrstop | |
18 | #endif | |
19 | ||
20 | #ifndef WX_PRECOMP | |
c21dcf80 VZ |
21 | #include "wx/app.h" |
22 | #include "wx/event.h" | |
ce45133e | 23 | #include "wx/scrolwin.h" |
c21dcf80 | 24 | #include "wx/window.h" |
1649d288 VZ |
25 | #endif // WX_PRECOMP |
26 | ||
3bad8c39 VZ |
27 | #include "wx/frame.h" |
28 | #include "wx/menu.h" | |
29 | ||
1649d288 VZ |
30 | #include "wx/scopeguard.h" |
31 | ||
32 | namespace | |
33 | { | |
34 | ||
35 | // this string will record the execution of all handlers | |
36 | wxString g_str; | |
37 | ||
38 | // a custom event | |
39 | wxDEFINE_EVENT(TEST_EVT, wxCommandEvent); | |
40 | ||
ce45133e VZ |
41 | // a custom event handler tracing the propagation of the events of the |
42 | // specified types | |
43 | template <class Event> | |
44 | class TestEvtHandlerBase : public wxEvtHandler | |
1649d288 VZ |
45 | { |
46 | public: | |
ce45133e VZ |
47 | TestEvtHandlerBase(wxEventType evtType, char tag) |
48 | : m_evtType(evtType), | |
49 | m_tag(tag) | |
1649d288 | 50 | { |
ce45133e VZ |
51 | Connect(evtType, |
52 | static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest)); | |
1649d288 VZ |
53 | } |
54 | ||
55 | // override ProcessEvent() to confirm that it is called for all event | |
56 | // handlers in the chain | |
57 | virtual bool ProcessEvent(wxEvent& event) | |
58 | { | |
ce45133e | 59 | if ( event.GetEventType() == m_evtType ) |
1649d288 VZ |
60 | g_str += 'o'; // "o" == "overridden" |
61 | ||
62 | return wxEvtHandler::ProcessEvent(event); | |
63 | } | |
64 | ||
65 | private: | |
ce45133e | 66 | void OnTest(wxEvent& event) |
1649d288 VZ |
67 | { |
68 | g_str += m_tag; | |
69 | ||
70 | event.Skip(); | |
71 | } | |
72 | ||
ce45133e | 73 | const wxEventType m_evtType; |
1649d288 VZ |
74 | const char m_tag; |
75 | ||
ce45133e VZ |
76 | wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase, Event); |
77 | }; | |
78 | ||
79 | struct TestEvtHandler : TestEvtHandlerBase<wxCommandEvent> | |
80 | { | |
81 | TestEvtHandler(char tag) | |
82 | : TestEvtHandlerBase<wxCommandEvent>(TEST_EVT, tag) | |
83 | { | |
84 | } | |
85 | }; | |
86 | ||
3bad8c39 VZ |
87 | struct TestMenuEvtHandler : TestEvtHandlerBase<wxCommandEvent> |
88 | { | |
89 | TestMenuEvtHandler(char tag) | |
90 | : TestEvtHandlerBase<wxCommandEvent>(wxEVT_MENU, tag) | |
91 | { | |
92 | } | |
93 | }; | |
94 | ||
ce45133e VZ |
95 | struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent> |
96 | { | |
97 | TestPaintEvtHandler(char tag) | |
98 | : TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag) | |
99 | { | |
100 | } | |
1649d288 VZ |
101 | }; |
102 | ||
103 | // a window handling the test event | |
104 | class TestWindow : public wxWindow | |
105 | { | |
106 | public: | |
107 | TestWindow(wxWindow *parent, char tag) | |
108 | : wxWindow(parent, wxID_ANY), | |
109 | m_tag(tag) | |
110 | { | |
111 | Connect(TEST_EVT, wxCommandEventHandler(TestWindow::OnTest)); | |
112 | } | |
113 | ||
114 | private: | |
115 | void OnTest(wxCommandEvent& event) | |
116 | { | |
117 | g_str += m_tag; | |
118 | ||
119 | event.Skip(); | |
120 | } | |
121 | ||
122 | const char m_tag; | |
123 | ||
124 | DECLARE_NO_COPY_CLASS(TestWindow) | |
125 | }; | |
126 | ||
ce45133e VZ |
127 | // a scroll window handling paint event: we want to have a special test case |
128 | // for this because the event propagation is complicated even further than | |
129 | // usual here by the presence of wxScrollHelperEvtHandler in the event handlers | |
130 | // chain and the fact that OnDraw() virtual method must be called if EVT_PAINT | |
131 | // is not handled | |
132 | class TestScrollWindow : public wxScrolledWindow | |
133 | { | |
134 | public: | |
135 | TestScrollWindow(wxWindow *parent) | |
136 | : wxScrolledWindow(parent, wxID_ANY) | |
137 | { | |
138 | Connect(wxEVT_PAINT, wxPaintEventHandler(TestScrollWindow::OnPaint)); | |
139 | } | |
140 | ||
141 | virtual void OnDraw(wxDC& WXUNUSED(dc)) | |
142 | { | |
143 | g_str += 'D'; // draw | |
144 | } | |
145 | ||
146 | private: | |
147 | void OnPaint(wxPaintEvent& event) | |
148 | { | |
149 | g_str += 'P'; // paint | |
150 | event.Skip(); | |
151 | } | |
152 | ||
153 | wxDECLARE_NO_COPY_CLASS(TestScrollWindow); | |
154 | }; | |
155 | ||
1649d288 VZ |
156 | int DoFilterEvent(wxEvent& event) |
157 | { | |
3bad8c39 VZ |
158 | if ( event.GetEventType() == TEST_EVT || |
159 | event.GetEventType() == wxEVT_MENU ) | |
1649d288 VZ |
160 | g_str += 'a'; |
161 | ||
162 | return -1; | |
163 | } | |
164 | ||
165 | bool DoProcessEvent(wxEvent& event) | |
166 | { | |
3bad8c39 VZ |
167 | if ( event.GetEventType() == TEST_EVT || |
168 | event.GetEventType() == wxEVT_MENU ) | |
1649d288 VZ |
169 | g_str += 'A'; |
170 | ||
171 | return false; | |
172 | } | |
173 | ||
174 | } // anonymous namespace | |
175 | ||
176 | // -------------------------------------------------------------------------- | |
177 | // test class | |
178 | // -------------------------------------------------------------------------- | |
179 | ||
180 | class EventPropagationTestCase : public CppUnit::TestCase | |
181 | { | |
182 | public: | |
183 | EventPropagationTestCase() {} | |
184 | ||
185 | virtual void setUp(); | |
186 | virtual void tearDown(); | |
187 | ||
188 | private: | |
189 | CPPUNIT_TEST_SUITE( EventPropagationTestCase ); | |
190 | CPPUNIT_TEST( OneHandler ); | |
191 | CPPUNIT_TEST( TwoHandlers ); | |
192 | CPPUNIT_TEST( WindowWithoutHandler ); | |
193 | CPPUNIT_TEST( WindowWithHandler ); | |
bbdee10d | 194 | CPPUNIT_TEST( ForwardEvent ); |
ce45133e VZ |
195 | CPPUNIT_TEST( ScrollWindowWithoutHandler ); |
196 | CPPUNIT_TEST( ScrollWindowWithHandler ); | |
3bad8c39 | 197 | CPPUNIT_TEST( MenuEvent ); |
1649d288 VZ |
198 | CPPUNIT_TEST_SUITE_END(); |
199 | ||
200 | void OneHandler(); | |
201 | void TwoHandlers(); | |
202 | void WindowWithoutHandler(); | |
203 | void WindowWithHandler(); | |
bbdee10d | 204 | void ForwardEvent(); |
ce45133e VZ |
205 | void ScrollWindowWithoutHandler(); |
206 | void ScrollWindowWithHandler(); | |
3bad8c39 | 207 | void MenuEvent(); |
1649d288 VZ |
208 | |
209 | DECLARE_NO_COPY_CLASS(EventPropagationTestCase) | |
210 | }; | |
211 | ||
212 | // register in the unnamed registry so that these tests are run by default | |
213 | CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase ); | |
214 | ||
e3778b4d | 215 | // also include in its own registry so that these tests can be run alone |
1649d288 VZ |
216 | CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EventPropagationTestCase, "EventPropagationTestCase" ); |
217 | ||
218 | void EventPropagationTestCase::setUp() | |
219 | { | |
220 | SetFilterEventFunc(DoFilterEvent); | |
221 | SetProcessEventFunc(DoProcessEvent); | |
222 | ||
223 | g_str.clear(); | |
224 | } | |
225 | ||
226 | void EventPropagationTestCase::tearDown() | |
227 | { | |
228 | SetFilterEventFunc(NULL); | |
229 | SetProcessEventFunc(NULL); | |
230 | } | |
231 | ||
232 | void EventPropagationTestCase::OneHandler() | |
233 | { | |
234 | wxCommandEvent event(TEST_EVT); | |
235 | TestEvtHandler h1('1'); | |
236 | h1.ProcessEvent(event); | |
237 | CPPUNIT_ASSERT_EQUAL( "oa1A", g_str ); | |
238 | } | |
239 | ||
240 | void EventPropagationTestCase::TwoHandlers() | |
241 | { | |
242 | wxCommandEvent event(TEST_EVT); | |
243 | TestEvtHandler h1('1'); | |
244 | TestEvtHandler h2('2'); | |
245 | h1.SetNextHandler(&h2); | |
246 | h2.SetPreviousHandler(&h1); | |
247 | h1.ProcessEvent(event); | |
248 | CPPUNIT_ASSERT_EQUAL( "oa1o2A", g_str ); | |
249 | } | |
250 | ||
251 | void EventPropagationTestCase::WindowWithoutHandler() | |
252 | { | |
253 | wxCommandEvent event(TEST_EVT); | |
254 | TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p'); | |
255 | wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy ); | |
256 | ||
257 | TestWindow * const child = new TestWindow(parent, 'c'); | |
258 | ||
004867db | 259 | child->GetEventHandler()->ProcessEvent(event); |
1649d288 VZ |
260 | CPPUNIT_ASSERT_EQUAL( "acpA", g_str ); |
261 | } | |
262 | ||
263 | void EventPropagationTestCase::WindowWithHandler() | |
264 | { | |
265 | wxCommandEvent event(TEST_EVT); | |
266 | TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p'); | |
267 | wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy ); | |
268 | ||
269 | TestWindow * const child = new TestWindow(parent, 'c'); | |
270 | ||
271 | TestEvtHandler h1('1'); | |
272 | child->PushEventHandler(&h1); | |
9ebfc963 | 273 | wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false ); |
1649d288 VZ |
274 | TestEvtHandler h2('2'); |
275 | child->PushEventHandler(&h2); | |
9ebfc963 | 276 | wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false ); |
1649d288 VZ |
277 | |
278 | child->HandleWindowEvent(event); | |
279 | CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str ); | |
280 | } | |
281 | ||
bbdee10d VZ |
282 | void EventPropagationTestCase::ForwardEvent() |
283 | { | |
284 | // The idea of this test is to check that the events explicitly forwarded | |
285 | // to another event handler still get pre/post-processed as usual as this | |
286 | // used to be broken by the fixes trying to avoid duplicate processing. | |
287 | TestWindow * const win = new TestWindow(wxTheApp->GetTopWindow(), 'w'); | |
288 | wxON_BLOCK_EXIT_OBJ0( *win, wxWindow::Destroy ); | |
289 | ||
290 | TestEvtHandler h1('1'); | |
291 | win->PushEventHandler(&h1); | |
292 | wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false ); | |
293 | ||
294 | class ForwardEvtHandler : public wxEvtHandler | |
295 | { | |
296 | public: | |
297 | ForwardEvtHandler(wxEvtHandler& h) : m_h(&h) { } | |
298 | ||
299 | virtual bool ProcessEvent(wxEvent& event) | |
300 | { | |
301 | g_str += 'f'; | |
302 | ||
303 | return m_h->ProcessEvent(event); | |
304 | } | |
305 | ||
306 | private: | |
307 | wxEvtHandler *m_h; | |
308 | } f(h1); | |
309 | ||
310 | // First send the event directly to f. | |
311 | wxCommandEvent event1(TEST_EVT); | |
312 | f.ProcessEvent(event1); | |
313 | CPPUNIT_ASSERT_EQUAL( "foa1wA", g_str ); | |
314 | g_str.clear(); | |
315 | ||
316 | // And then also test sending it to f indirectly. | |
317 | wxCommandEvent event2(TEST_EVT); | |
318 | TestEvtHandler h2('2'); | |
319 | h2.SetNextHandler(&f); | |
320 | h2.ProcessEvent(event2); | |
321 | CPPUNIT_ASSERT_EQUAL( "oa2fo1wAA", g_str ); | |
322 | } | |
323 | ||
ce45133e VZ |
324 | void EventPropagationTestCase::ScrollWindowWithoutHandler() |
325 | { | |
52212bcb VZ |
326 | TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p'); |
327 | wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy ); | |
328 | ||
329 | TestScrollWindow * const win = new TestScrollWindow(parent); | |
ce45133e | 330 | |
33a190be | 331 | #if !defined(__WXOSX__) && !defined(__WXGTK3__) |
ce45133e VZ |
332 | wxPaintEvent event(win->GetId()); |
333 | win->ProcessWindowEvent(event); | |
334 | CPPUNIT_ASSERT_EQUAL( "PD", g_str ); | |
f009da6e | 335 | #endif |
52212bcb VZ |
336 | g_str.clear(); |
337 | wxCommandEvent eventCmd(TEST_EVT); | |
338 | win->HandleWindowEvent(eventCmd); | |
339 | CPPUNIT_ASSERT_EQUAL( "apA", g_str ); | |
ce45133e VZ |
340 | } |
341 | ||
342 | void EventPropagationTestCase::ScrollWindowWithHandler() | |
343 | { | |
52212bcb VZ |
344 | TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p'); |
345 | wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy ); | |
346 | ||
347 | TestScrollWindow * const win = new TestScrollWindow(parent); | |
ce45133e | 348 | |
33a190be | 349 | #if !defined(__WXOSX__) && !defined(__WXGTK3__) |
ce45133e VZ |
350 | TestPaintEvtHandler h('h'); |
351 | win->PushEventHandler(&h); | |
352 | wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false ); | |
353 | ||
354 | wxPaintEvent event(win->GetId()); | |
355 | win->ProcessWindowEvent(event); | |
356 | CPPUNIT_ASSERT_EQUAL( "ohPD", g_str ); | |
15536294 | 357 | #endif |
52212bcb VZ |
358 | |
359 | g_str.clear(); | |
360 | wxCommandEvent eventCmd(TEST_EVT); | |
361 | win->HandleWindowEvent(eventCmd); | |
362 | CPPUNIT_ASSERT_EQUAL( "apA", g_str ); | |
ce45133e VZ |
363 | } |
364 | ||
3bad8c39 VZ |
365 | // Helper for checking that the menu event processing resulted in the expected |
366 | // output from the handlers. | |
367 | void CheckMenuEvent(wxMenu* menu, const char* expected) | |
368 | { | |
369 | g_str.clear(); | |
370 | ||
371 | // Trigger the menu event: this is more reliable than using | |
372 | // wxUIActionSimulator and currently works in all ports as they all call | |
373 | // wxMenuBase::SendEvent() from their respective menu event handlers. | |
374 | menu->SendEvent(wxID_NEW); | |
375 | ||
376 | CPPUNIT_ASSERT_EQUAL( expected, g_str ); | |
377 | } | |
378 | ||
379 | void EventPropagationTestCase::MenuEvent() | |
380 | { | |
381 | // Create a minimal menu bar. | |
382 | wxMenu* const menu = new wxMenu; | |
383 | menu->Append(wxID_NEW); | |
384 | wxMenuBar* const mb = new wxMenuBar; | |
385 | mb->Append(menu, "&Menu"); | |
386 | ||
387 | wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow()); | |
388 | frame->SetMenuBar(mb); | |
389 | wxON_BLOCK_EXIT_OBJ1( *frame, wxFrame::SetMenuBar, (wxMenuBar*)NULL ); | |
390 | ||
391 | // Check that wxApp gets the event exactly once. | |
392 | CheckMenuEvent( menu, "aA" ); | |
393 | ||
394 | ||
395 | // Check that the menu event handler is called. | |
396 | TestMenuEvtHandler hm('m'); // 'm' for "menu" | |
397 | menu->SetNextHandler(&hm); | |
398 | wxON_BLOCK_EXIT_OBJ1( *menu, | |
399 | wxEvtHandler::SetNextHandler, (wxEvtHandler*)NULL ); | |
400 | CheckMenuEvent( menu, "aomA" ); | |
401 | ||
402 | ||
4ed3f4ab VZ |
403 | // Test that the event handler associated with the menu bar gets the event. |
404 | TestMenuEvtHandler hb('b'); // 'b' for "menu Bar" | |
405 | mb->PushEventHandler(&hb); | |
406 | wxON_BLOCK_EXIT_OBJ1( *mb, wxWindow::PopEventHandler, false ); | |
407 | ||
408 | CheckMenuEvent( menu, "aomobA" ); | |
409 | ||
410 | ||
3bad8c39 VZ |
411 | // Also test that the window to which the menu belongs gets the event. |
412 | TestMenuEvtHandler hw('w'); // 'w' for "Window" | |
413 | frame->PushEventHandler(&hw); | |
414 | wxON_BLOCK_EXIT_OBJ1( *frame, wxWindow::PopEventHandler, false ); | |
415 | ||
4ed3f4ab | 416 | CheckMenuEvent( menu, "aomobowA" ); |
3bad8c39 | 417 | } |