]> git.saurik.com Git - wxWidgets.git/blob - tests/events/propagation.cpp
Avoid crashes in wxMSW when using buttons without valid parent.
[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/scopeguard.h"
28
29 namespace
30 {
31
32 // this string will record the execution of all handlers
33 wxString g_str;
34
35 // a custom event
36 wxDEFINE_EVENT(TEST_EVT, wxCommandEvent);
37
38 // a custom event handler tracing the propagation of the events of the
39 // specified types
40 template <class Event>
41 class TestEvtHandlerBase : public wxEvtHandler
42 {
43 public:
44 TestEvtHandlerBase(wxEventType evtType, char tag)
45 : m_evtType(evtType),
46 m_tag(tag)
47 {
48 Connect(evtType,
49 static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest));
50 }
51
52 // override ProcessEvent() to confirm that it is called for all event
53 // handlers in the chain
54 virtual bool ProcessEvent(wxEvent& event)
55 {
56 if ( event.GetEventType() == m_evtType )
57 g_str += 'o'; // "o" == "overridden"
58
59 return wxEvtHandler::ProcessEvent(event);
60 }
61
62 private:
63 void OnTest(wxEvent& event)
64 {
65 g_str += m_tag;
66
67 event.Skip();
68 }
69
70 const wxEventType m_evtType;
71 const char m_tag;
72
73 wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase, Event);
74 };
75
76 struct TestEvtHandler : TestEvtHandlerBase<wxCommandEvent>
77 {
78 TestEvtHandler(char tag)
79 : TestEvtHandlerBase<wxCommandEvent>(TEST_EVT, tag)
80 {
81 }
82 };
83
84 struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
85 {
86 TestPaintEvtHandler(char tag)
87 : TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag)
88 {
89 }
90 };
91
92 // a window handling the test event
93 class TestWindow : public wxWindow
94 {
95 public:
96 TestWindow(wxWindow *parent, char tag)
97 : wxWindow(parent, wxID_ANY),
98 m_tag(tag)
99 {
100 Connect(TEST_EVT, wxCommandEventHandler(TestWindow::OnTest));
101 }
102
103 private:
104 void OnTest(wxCommandEvent& event)
105 {
106 g_str += m_tag;
107
108 event.Skip();
109 }
110
111 const char m_tag;
112
113 DECLARE_NO_COPY_CLASS(TestWindow)
114 };
115
116 // a scroll window handling paint event: we want to have a special test case
117 // for this because the event propagation is complicated even further than
118 // usual here by the presence of wxScrollHelperEvtHandler in the event handlers
119 // chain and the fact that OnDraw() virtual method must be called if EVT_PAINT
120 // is not handled
121 class TestScrollWindow : public wxScrolledWindow
122 {
123 public:
124 TestScrollWindow(wxWindow *parent)
125 : wxScrolledWindow(parent, wxID_ANY)
126 {
127 Connect(wxEVT_PAINT, wxPaintEventHandler(TestScrollWindow::OnPaint));
128 }
129
130 virtual void OnDraw(wxDC& WXUNUSED(dc))
131 {
132 g_str += 'D'; // draw
133 }
134
135 private:
136 void OnPaint(wxPaintEvent& event)
137 {
138 g_str += 'P'; // paint
139 event.Skip();
140 }
141
142 wxDECLARE_NO_COPY_CLASS(TestScrollWindow);
143 };
144
145 int DoFilterEvent(wxEvent& event)
146 {
147 if ( event.GetEventType() == TEST_EVT )
148 g_str += 'a';
149
150 return -1;
151 }
152
153 bool DoProcessEvent(wxEvent& event)
154 {
155 if ( event.GetEventType() == TEST_EVT )
156 g_str += 'A';
157
158 return false;
159 }
160
161 } // anonymous namespace
162
163 // --------------------------------------------------------------------------
164 // test class
165 // --------------------------------------------------------------------------
166
167 class EventPropagationTestCase : public CppUnit::TestCase
168 {
169 public:
170 EventPropagationTestCase() {}
171
172 virtual void setUp();
173 virtual void tearDown();
174
175 private:
176 CPPUNIT_TEST_SUITE( EventPropagationTestCase );
177 CPPUNIT_TEST( OneHandler );
178 CPPUNIT_TEST( TwoHandlers );
179 CPPUNIT_TEST( WindowWithoutHandler );
180 CPPUNIT_TEST( WindowWithHandler );
181 CPPUNIT_TEST( ForwardEvent );
182 CPPUNIT_TEST( ScrollWindowWithoutHandler );
183 CPPUNIT_TEST( ScrollWindowWithHandler );
184 CPPUNIT_TEST_SUITE_END();
185
186 void OneHandler();
187 void TwoHandlers();
188 void WindowWithoutHandler();
189 void WindowWithHandler();
190 void ForwardEvent();
191 void ScrollWindowWithoutHandler();
192 void ScrollWindowWithHandler();
193
194 DECLARE_NO_COPY_CLASS(EventPropagationTestCase)
195 };
196
197 // register in the unnamed registry so that these tests are run by default
198 CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase );
199
200 // also include in its own registry so that these tests can be run alone
201 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EventPropagationTestCase, "EventPropagationTestCase" );
202
203 void EventPropagationTestCase::setUp()
204 {
205 SetFilterEventFunc(DoFilterEvent);
206 SetProcessEventFunc(DoProcessEvent);
207
208 g_str.clear();
209 }
210
211 void EventPropagationTestCase::tearDown()
212 {
213 SetFilterEventFunc(NULL);
214 SetProcessEventFunc(NULL);
215 }
216
217 void EventPropagationTestCase::OneHandler()
218 {
219 wxCommandEvent event(TEST_EVT);
220 TestEvtHandler h1('1');
221 h1.ProcessEvent(event);
222 CPPUNIT_ASSERT_EQUAL( "oa1A", g_str );
223 }
224
225 void EventPropagationTestCase::TwoHandlers()
226 {
227 wxCommandEvent event(TEST_EVT);
228 TestEvtHandler h1('1');
229 TestEvtHandler h2('2');
230 h1.SetNextHandler(&h2);
231 h2.SetPreviousHandler(&h1);
232 h1.ProcessEvent(event);
233 CPPUNIT_ASSERT_EQUAL( "oa1o2A", g_str );
234 }
235
236 void EventPropagationTestCase::WindowWithoutHandler()
237 {
238 wxCommandEvent event(TEST_EVT);
239 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
240 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
241
242 TestWindow * const child = new TestWindow(parent, 'c');
243
244 child->GetEventHandler()->ProcessEvent(event);
245 CPPUNIT_ASSERT_EQUAL( "acpA", g_str );
246 }
247
248 void EventPropagationTestCase::WindowWithHandler()
249 {
250 wxCommandEvent event(TEST_EVT);
251 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
252 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
253
254 TestWindow * const child = new TestWindow(parent, 'c');
255
256 TestEvtHandler h1('1');
257 child->PushEventHandler(&h1);
258 wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
259 TestEvtHandler h2('2');
260 child->PushEventHandler(&h2);
261 wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
262
263 child->HandleWindowEvent(event);
264 CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str );
265 }
266
267 void EventPropagationTestCase::ForwardEvent()
268 {
269 // The idea of this test is to check that the events explicitly forwarded
270 // to another event handler still get pre/post-processed as usual as this
271 // used to be broken by the fixes trying to avoid duplicate processing.
272 TestWindow * const win = new TestWindow(wxTheApp->GetTopWindow(), 'w');
273 wxON_BLOCK_EXIT_OBJ0( *win, wxWindow::Destroy );
274
275 TestEvtHandler h1('1');
276 win->PushEventHandler(&h1);
277 wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
278
279 class ForwardEvtHandler : public wxEvtHandler
280 {
281 public:
282 ForwardEvtHandler(wxEvtHandler& h) : m_h(&h) { }
283
284 virtual bool ProcessEvent(wxEvent& event)
285 {
286 g_str += 'f';
287
288 return m_h->ProcessEvent(event);
289 }
290
291 private:
292 wxEvtHandler *m_h;
293 } f(h1);
294
295 // First send the event directly to f.
296 wxCommandEvent event1(TEST_EVT);
297 f.ProcessEvent(event1);
298 CPPUNIT_ASSERT_EQUAL( "foa1wA", g_str );
299 g_str.clear();
300
301 // And then also test sending it to f indirectly.
302 wxCommandEvent event2(TEST_EVT);
303 TestEvtHandler h2('2');
304 h2.SetNextHandler(&f);
305 h2.ProcessEvent(event2);
306 CPPUNIT_ASSERT_EQUAL( "oa2fo1wAA", g_str );
307 }
308
309 void EventPropagationTestCase::ScrollWindowWithoutHandler()
310 {
311 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
312 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
313
314 TestScrollWindow * const win = new TestScrollWindow(parent);
315
316 #ifndef __WXOSX__
317 wxPaintEvent event(win->GetId());
318 win->ProcessWindowEvent(event);
319 CPPUNIT_ASSERT_EQUAL( "PD", g_str );
320 #endif
321 g_str.clear();
322 wxCommandEvent eventCmd(TEST_EVT);
323 win->HandleWindowEvent(eventCmd);
324 CPPUNIT_ASSERT_EQUAL( "apA", g_str );
325 }
326
327 void EventPropagationTestCase::ScrollWindowWithHandler()
328 {
329 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
330 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
331
332 TestScrollWindow * const win = new TestScrollWindow(parent);
333
334 #ifndef __WXOSX__
335 TestPaintEvtHandler h('h');
336 win->PushEventHandler(&h);
337 wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
338
339 wxPaintEvent event(win->GetId());
340 win->ProcessWindowEvent(event);
341 CPPUNIT_ASSERT_EQUAL( "ohPD", g_str );
342 #endif
343
344 g_str.clear();
345 wxCommandEvent eventCmd(TEST_EVT);
346 win->HandleWindowEvent(eventCmd);
347 CPPUNIT_ASSERT_EQUAL( "apA", g_str );
348 }
349