]> git.saurik.com Git - wxWidgets.git/blob - tests/events/propagation.cpp
0a9222f7e0fd1a80b0e5b45490e472f17723d996
[wxWidgets.git] / tests / events / propagation.cpp
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
21 #include "wx/app.h"
22 #include "wx/event.h"
23 #include "wx/scrolwin.h"
24 #include "wx/window.h"
25 #endif // WX_PRECOMP
26
27 #include "wx/frame.h"
28 #include "wx/menu.h"
29 #include "wx/scopedptr.h"
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
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
45 {
46 public:
47 TestEvtHandlerBase(wxEventType evtType, char tag)
48 : m_evtType(evtType),
49 m_tag(tag)
50 {
51 Connect(evtType,
52 static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest));
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 {
59 if ( event.GetEventType() == m_evtType )
60 g_str += 'o'; // "o" == "overridden"
61
62 return wxEvtHandler::ProcessEvent(event);
63 }
64
65 private:
66 void OnTest(wxEvent& event)
67 {
68 g_str += m_tag;
69
70 event.Skip();
71 }
72
73 const wxEventType m_evtType;
74 const char m_tag;
75
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
87 struct TestMenuEvtHandler : TestEvtHandlerBase<wxCommandEvent>
88 {
89 TestMenuEvtHandler(char tag)
90 : TestEvtHandlerBase<wxCommandEvent>(wxEVT_MENU, tag)
91 {
92 }
93 };
94
95 struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
96 {
97 TestPaintEvtHandler(char tag)
98 : TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag)
99 {
100 }
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
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
156 int DoFilterEvent(wxEvent& event)
157 {
158 if ( event.GetEventType() == TEST_EVT ||
159 event.GetEventType() == wxEVT_MENU )
160 g_str += 'a';
161
162 return -1;
163 }
164
165 bool DoProcessEvent(wxEvent& event)
166 {
167 if ( event.GetEventType() == TEST_EVT ||
168 event.GetEventType() == wxEVT_MENU )
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 );
194 CPPUNIT_TEST( ForwardEvent );
195 CPPUNIT_TEST( ScrollWindowWithoutHandler );
196 CPPUNIT_TEST( ScrollWindowWithHandler );
197 CPPUNIT_TEST( MenuEvent );
198 CPPUNIT_TEST_SUITE_END();
199
200 void OneHandler();
201 void TwoHandlers();
202 void WindowWithoutHandler();
203 void WindowWithHandler();
204 void ForwardEvent();
205 void ScrollWindowWithoutHandler();
206 void ScrollWindowWithHandler();
207 void MenuEvent();
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
215 // also include in its own registry so that these tests can be run alone
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
259 child->GetEventHandler()->ProcessEvent(event);
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);
273 wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
274 TestEvtHandler h2('2');
275 child->PushEventHandler(&h2);
276 wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
277
278 child->HandleWindowEvent(event);
279 CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str );
280 }
281
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
324 void EventPropagationTestCase::ScrollWindowWithoutHandler()
325 {
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);
330
331 #if !defined(__WXOSX__) && !defined(__WXGTK3__)
332 wxPaintEvent event(win->GetId());
333 win->ProcessWindowEvent(event);
334 CPPUNIT_ASSERT_EQUAL( "PD", g_str );
335 #endif
336 g_str.clear();
337 wxCommandEvent eventCmd(TEST_EVT);
338 win->HandleWindowEvent(eventCmd);
339 CPPUNIT_ASSERT_EQUAL( "apA", g_str );
340 }
341
342 void EventPropagationTestCase::ScrollWindowWithHandler()
343 {
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);
348
349 #if !defined(__WXOSX__) && !defined(__WXGTK3__)
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 );
357 #endif
358
359 g_str.clear();
360 wxCommandEvent eventCmd(TEST_EVT);
361 win->HandleWindowEvent(eventCmd);
362 CPPUNIT_ASSERT_EQUAL( "apA", g_str );
363 }
364
365 // Create a menu bar with a single menu containing wxID_APPLY menu item and
366 // attach it to the specified frame.
367 wxMenu* CreateTestMenu(wxFrame* frame)
368 {
369 wxMenu* const menu = new wxMenu;
370 menu->Append(wxID_APPLY);
371 wxMenuBar* const mb = new wxMenuBar;
372 mb->Append(menu, "&Menu");
373 frame->SetMenuBar(mb);
374
375 return menu;
376 }
377
378 // Helper for checking that the menu event processing resulted in the expected
379 // output from the handlers.
380 //
381 // Notice that this is supposed to be used with ASSERT_MENU_EVENT_RESULT()
382 // macro to make the file name and line number of the caller appear in the
383 // failure messages.
384 void
385 CheckMenuEvent(wxMenu* menu, const char* result, CppUnit::SourceLine sourceLine)
386 {
387 g_str.clear();
388
389 // Trigger the menu event: this is more reliable than using
390 // wxUIActionSimulator and currently works in all ports as they all call
391 // wxMenuBase::SendEvent() from their respective menu event handlers.
392 menu->SendEvent(wxID_APPLY);
393
394 CPPUNIT_NS::assertEquals( result, g_str, sourceLine, "" );
395 }
396
397 #define ASSERT_MENU_EVENT_RESULT(menu, result) \
398 CheckMenuEvent((menu), (result), CPPUNIT_SOURCELINE())
399
400 void EventPropagationTestCase::MenuEvent()
401 {
402 wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow());
403
404 // Create a minimal menu bar.
405 wxMenu* const menu = CreateTestMenu(frame);
406 wxMenuBar* const mb = menu->GetMenuBar();
407 wxScopedPtr<wxMenuBar> ensureMenuBarDestruction(mb);
408 wxON_BLOCK_EXIT_OBJ1( *frame, wxFrame::SetMenuBar, (wxMenuBar*)NULL );
409
410 // Check that wxApp gets the event exactly once.
411 ASSERT_MENU_EVENT_RESULT( menu, "aA" );
412
413
414 // Check that the menu event handler is called.
415 TestMenuEvtHandler hm('m'); // 'm' for "menu"
416 menu->SetNextHandler(&hm);
417 wxON_BLOCK_EXIT_OBJ1( *menu,
418 wxEvtHandler::SetNextHandler, (wxEvtHandler*)NULL );
419 ASSERT_MENU_EVENT_RESULT( menu, "aomA" );
420
421
422 // Test that the event handler associated with the menu bar gets the event.
423 TestMenuEvtHandler hb('b'); // 'b' for "menu Bar"
424 mb->PushEventHandler(&hb);
425 wxON_BLOCK_EXIT_OBJ1( *mb, wxWindow::PopEventHandler, false );
426
427 ASSERT_MENU_EVENT_RESULT( menu, "aomobA" );
428
429
430 // Also test that the window to which the menu belongs gets the event.
431 TestMenuEvtHandler hw('w'); // 'w' for "Window"
432 frame->PushEventHandler(&hw);
433 wxON_BLOCK_EXIT_OBJ1( *frame, wxWindow::PopEventHandler, false );
434
435 ASSERT_MENU_EVENT_RESULT( menu, "aomobowA" );
436 }