]>
Commit | Line | Data |
---|---|---|
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 it's 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 |