]> git.saurik.com Git - wxWidgets.git/blob - tests/events/evthandler.cpp
aff698a67b108fb3261fe1c0444df9b39be17f30
[wxWidgets.git] / tests / events / evthandler.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/events/evthandler.cpp
3 // Purpose: Test the new event types and wxEvtHandler-methods
4 // Author: Peter Most
5 // Created: 2009-01-24
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Peter Most
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ----------------------------------------------------------------------------
11 // headers
12 // ----------------------------------------------------------------------------
13
14 #include "testprec.h"
15
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19
20 #include "wx/event.h"
21
22 // ----------------------------------------------------------------------------
23 // test events and their handlers
24 // ----------------------------------------------------------------------------
25
26 const wxEventType LegacyEventType = wxNewEventType();
27
28 class MyEvent;
29 wxDEFINE_EVENT(MyEventType, MyEvent);
30
31 class MyEvent : public wxEvent
32 {
33 public:
34 MyEvent() : wxEvent(0, MyEventType) { }
35
36 virtual wxEvent *Clone() const { return new MyEvent; }
37 };
38
39 typedef void (wxEvtHandler::*MyEventFunction)(MyEvent&);
40 #ifndef wxHAS_EVENT_BIND
41 #define MyEventHandler(func) wxEVENT_HANDLER_CAST(MyEventFunction, func)
42 #else
43 #define MyEventHandler(func) &func
44 #endif
45 #define EVT_MYEVENT(func) \
46 wx__DECLARE_EVT0(MyEventType, MyEventHandler(func))
47
48 class AnotherEvent : public wxEvent
49 {
50 };
51
52 namespace
53 {
54
55 struct Called
56 {
57 Called() { Reset(); }
58
59 void Reset()
60 {
61 function =
62 functor =
63 method =
64 smethod = false;
65 }
66
67 bool function,
68 functor,
69 method,
70 smethod;
71 } g_called;
72
73 void GlobalOnMyEvent(MyEvent&)
74 {
75 g_called.function = true;
76 }
77
78 void GlobalOnEvent(wxEvent&)
79 {
80 g_called.function = true;
81 }
82
83 #ifdef TEST_INVALID_BIND_GLOBAL
84 void GlobalOnAnotherEvent(AnotherEvent&);
85 #endif
86
87 void GlobalOnIdle(wxIdleEvent&)
88 {
89 g_called.function = true;
90 }
91
92 struct MyFunctor
93 {
94 void operator()(MyEvent &) { g_called.functor = true; }
95 };
96
97 struct IdleFunctor
98 {
99 void operator()(wxIdleEvent &) { g_called.functor = true; }
100 };
101
102 class MyHandler : public wxEvtHandler
103 {
104 public:
105 static void StaticOnMyEvent(MyEvent &) { g_called.smethod = true; }
106 static void StaticOnAnotherEvent(AnotherEvent &);
107 static void StaticOnIdle(wxIdleEvent&) { g_called.smethod = true; }
108
109 void OnMyEvent(MyEvent&) { g_called.method = true; }
110 void OnEvent(wxEvent&) { g_called.method = true; }
111 void OnAnotherEvent(AnotherEvent&);
112 void OnIdle(wxIdleEvent&) { g_called.method = true; }
113 };
114
115 // we can also handle events in classes not deriving from wxEvtHandler
116 struct MySink
117 {
118 void OnMyEvent(MyEvent&) { g_called.method = true; }
119 void OnEvent(wxEvent&) { g_called.method = true; }
120 void OnIdle(wxIdleEvent&) { g_called.method = true; }
121 };
122
123 // also test event table compilation
124 class MyClassWithEventTable : public wxEvtHandler
125 {
126 public:
127 void OnMyEvent(MyEvent&) { g_called.method = true; }
128 void OnEvent(wxEvent&) { g_called.method = true; }
129 void OnAnotherEvent(AnotherEvent&);
130 void OnIdle(wxIdleEvent&) { g_called.method = true; }
131
132 private:
133 DECLARE_EVENT_TABLE()
134 };
135
136 BEGIN_EVENT_TABLE(MyClassWithEventTable, wxEvtHandler)
137 EVT_IDLE(MyClassWithEventTable::OnIdle)
138
139 EVT_MYEVENT(MyClassWithEventTable::OnMyEvent)
140 #ifdef wxHAS_EVENT_BIND
141 EVT_MYEVENT(MyClassWithEventTable::OnEvent)
142 #endif
143
144 // this shouldn't compile:
145 //EVT_MYEVENT(MyClassWithEventTable::OnIdle)
146 //EVT_IDLE(MyClassWithEventTable::OnAnotherEvent)
147 END_EVENT_TABLE()
148
149 } // anonymous namespace
150
151
152 // --------------------------------------------------------------------------
153 // test class
154 // --------------------------------------------------------------------------
155
156 class EvtHandlerTestCase : public CppUnit::TestCase
157 {
158 public:
159 EvtHandlerTestCase() {}
160
161 private:
162 CPPUNIT_TEST_SUITE( EvtHandlerTestCase );
163 CPPUNIT_TEST( BuiltinConnect );
164 CPPUNIT_TEST( LegacyConnect );
165 CPPUNIT_TEST( DisconnectWildcard );
166 CPPUNIT_TEST( AutoDisconnect );
167 #ifdef wxHAS_EVENT_BIND
168 CPPUNIT_TEST( BindFunction );
169 CPPUNIT_TEST( BindStaticMethod );
170 CPPUNIT_TEST( BindFunctor );
171 CPPUNIT_TEST( BindMethod );
172 CPPUNIT_TEST( BindMethodUsingBaseEvent );
173 CPPUNIT_TEST( BindFunctionUsingBaseEvent );
174 CPPUNIT_TEST( BindNonHandler );
175 CPPUNIT_TEST( InvalidBind );
176 #endif // wxHAS_EVENT_BIND
177 CPPUNIT_TEST_SUITE_END();
178
179 void BuiltinConnect();
180 void LegacyConnect();
181 void DisconnectWildcard();
182 void AutoDisconnect();
183 #ifdef wxHAS_EVENT_BIND
184 void BindFunction();
185 void BindStaticMethod();
186 void BindFunctor();
187 void BindMethod();
188 void BindMethodUsingBaseEvent();
189 void BindFunctionUsingBaseEvent();
190 void BindNonHandler();
191 void InvalidBind();
192 #endif // wxHAS_EVENT_BIND
193
194
195 // these member variables exceptionally don't use "m_" prefix because
196 // they're used so many times
197 MyHandler handler;
198 MyEvent e;
199
200 DECLARE_NO_COPY_CLASS(EvtHandlerTestCase)
201 };
202
203 // register in the unnamed registry so that these tests are run by default
204 CPPUNIT_TEST_SUITE_REGISTRATION( EvtHandlerTestCase );
205
206 // also include in its own registry so that these tests can be run alone
207 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtHandlerTestCase, "EvtHandlerTestCase" );
208
209 void EvtHandlerTestCase::BuiltinConnect()
210 {
211 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
212 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
213
214 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
215 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
216
217 // using casts like this is even uglier than using wxIdleEventHandler but
218 // it should still continue to work for compatibility
219 handler.Connect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
220 handler.Disconnect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
221
222 #ifdef wxHAS_EVENT_BIND
223 handler.Bind(wxEVT_IDLE, GlobalOnIdle);
224 handler.Unbind(wxEVT_IDLE, GlobalOnIdle);
225
226 IdleFunctor f;
227 handler.Bind(wxEVT_IDLE, f);
228 handler.Unbind(wxEVT_IDLE, f);
229
230 handler.Bind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
231 handler.Unbind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
232
233 handler.Bind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
234 handler.Unbind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
235 #endif // wxHAS_EVENT_BIND
236 }
237
238 void EvtHandlerTestCase::LegacyConnect()
239 {
240 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
241 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
242 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
243
244 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
245 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
246 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
247
248
249 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
250 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
251 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
252
253 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
254 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
255 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
256 }
257
258 void EvtHandlerTestCase::DisconnectWildcard()
259 {
260 // should be able to disconnect a different handler using "wildcard search"
261 MyHandler sink;
262 wxEvtHandler source;
263 source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink);
264 CPPUNIT_ASSERT(source.Disconnect(wxID_ANY, wxEVT_IDLE));
265 // destruction of source and sink here should properly clean up the
266 // wxEventConnectionRef without crashing
267 }
268
269 void EvtHandlerTestCase::AutoDisconnect()
270 {
271 wxEvtHandler source;
272 {
273 MyHandler sink;
274 source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink);
275 // mismatched event type, so nothing should be disconnected
276 CPPUNIT_ASSERT(!source.Disconnect(wxEVT_THREAD, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink));
277 }
278 // destruction of sink should have automatically disconnected it, so
279 // there should be nothing to disconnect anymore
280 CPPUNIT_ASSERT(!source.Disconnect(wxID_ANY, wxEVT_IDLE));
281 }
282
283 #ifdef wxHAS_EVENT_BIND
284
285 void EvtHandlerTestCase::BindFunction()
286 {
287 // function tests
288 handler.Bind( MyEventType, GlobalOnMyEvent );
289 g_called.Reset();
290 handler.ProcessEvent(e);
291 CPPUNIT_ASSERT( g_called.function );
292 handler.Unbind( MyEventType, GlobalOnMyEvent );
293 g_called.Reset();
294 handler.ProcessEvent(e);
295 CPPUNIT_ASSERT( !g_called.function ); // check that it was disconnected
296
297 handler.Bind( MyEventType, GlobalOnMyEvent, 0 );
298 handler.Unbind( MyEventType, GlobalOnMyEvent, 0 );
299
300 handler.Bind( MyEventType, GlobalOnMyEvent, 0, 0 );
301 handler.Unbind( MyEventType, GlobalOnMyEvent, 0, 0 );
302 }
303
304 void EvtHandlerTestCase::BindStaticMethod()
305 {
306 // static method tests (this is same as functions but still test it just in
307 // case we hit some strange compiler bugs)
308 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent );
309 g_called.Reset();
310 handler.ProcessEvent(e);
311 CPPUNIT_ASSERT( g_called.smethod );
312 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent );
313 g_called.Reset();
314 handler.ProcessEvent(e);
315 CPPUNIT_ASSERT( !g_called.smethod );
316
317 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
318 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
319
320 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
321 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
322 }
323
324 void EvtHandlerTestCase::BindFunctor()
325 {
326 // generalized functor tests
327 MyFunctor functor;
328
329 handler.Bind( MyEventType, functor );
330 g_called.Reset();
331 handler.ProcessEvent(e);
332 CPPUNIT_ASSERT( g_called.functor );
333 handler.Unbind( MyEventType, functor );
334 g_called.Reset();
335 handler.ProcessEvent(e);
336 CPPUNIT_ASSERT( !g_called.functor );
337
338 handler.Bind( MyEventType, functor, 0 );
339 handler.Unbind( MyEventType, functor, 0 );
340
341 handler.Bind( MyEventType, functor, 0, 0 );
342 handler.Unbind( MyEventType, functor, 0, 0 );
343
344 // test that a temporary functor is working as well and also test that
345 // unbinding a different (though equal) instance of the same functor does
346 // not work
347 MyFunctor func;
348 handler.Bind( MyEventType, MyFunctor() );
349 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, func ));
350
351 handler.Bind( MyEventType, MyFunctor(), 0 );
352 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, func, 0 ));
353
354 handler.Bind( MyEventType, MyFunctor(), 0, 0 );
355 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, func, 0, 0 ));
356 }
357
358 void EvtHandlerTestCase::BindMethod()
359 {
360 // class method tests
361 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler );
362 g_called.Reset();
363 handler.ProcessEvent(e);
364 CPPUNIT_ASSERT( g_called.method );
365 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler );
366 g_called.Reset();
367 handler.ProcessEvent(e);
368 CPPUNIT_ASSERT( !g_called.method );
369
370 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
371 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
372
373 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
374 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
375 }
376
377 void EvtHandlerTestCase::BindMethodUsingBaseEvent()
378 {
379 // test connecting a method taking just wxEvent and not MyEvent: this
380 // should work too if we don't need any MyEvent-specific information in the
381 // handler
382 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler );
383 g_called.Reset();
384 handler.ProcessEvent(e);
385 CPPUNIT_ASSERT( g_called.method );
386 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler );
387 g_called.Reset();
388 handler.ProcessEvent(e);
389 CPPUNIT_ASSERT( !g_called.method );
390
391 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
392 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
393
394 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
395 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
396 }
397
398
399 void EvtHandlerTestCase::BindFunctionUsingBaseEvent()
400 {
401 // test connecting a function taking just wxEvent and not MyEvent: this
402 // should work too if we don't need any MyEvent-specific information in the
403 // handler
404 handler.Bind( MyEventType, GlobalOnEvent );
405 g_called.Reset();
406 handler.ProcessEvent(e);
407 CPPUNIT_ASSERT( g_called.function );
408 handler.Unbind( MyEventType, GlobalOnEvent );
409 g_called.Reset();
410 handler.ProcessEvent(e);
411 CPPUNIT_ASSERT( !g_called.function );
412
413 handler.Bind( MyEventType, GlobalOnEvent, 0 );
414 handler.Unbind( MyEventType, GlobalOnEvent, 0 );
415
416 handler.Bind( MyEventType, GlobalOnEvent, 0, 0 );
417 handler.Unbind( MyEventType, GlobalOnEvent, 0, 0 );
418 }
419
420
421
422 void EvtHandlerTestCase::BindNonHandler()
423 {
424 // class method tests for class not derived from wxEvtHandler
425 MySink sink;
426
427 handler.Bind( MyEventType, &MySink::OnMyEvent, &sink );
428 g_called.Reset();
429 handler.ProcessEvent(e);
430 CPPUNIT_ASSERT( g_called.method );
431 handler.Unbind( MyEventType, &MySink::OnMyEvent, &sink );
432 g_called.Reset();
433 handler.ProcessEvent(e);
434 CPPUNIT_ASSERT( !g_called.method );
435 }
436
437 void EvtHandlerTestCase::InvalidBind()
438 {
439 // these calls shouldn't compile but we unfortunately can't check this
440 // automatically, you need to uncomment them manually and test that
441 // compilation does indeed fail
442
443 // connecting a handler with incompatible signature shouldn't work
444 #ifdef TEST_INVALID_BIND_GLOBAL
445 handler.Bind(MyEventType, GlobalOnAnotherEvent);
446 #endif
447 #ifdef TEST_INVALID_BIND_STATIC
448 handler.Bind(MyEventType, &MyHandler::StaticOnAnotherEvent);
449 #endif
450 #ifdef TEST_INVALID_BIND_METHOD
451 handler.Bind(MyEventType, &MyHandler::OnAnotherEvent, &handler);
452 #endif
453 #ifdef TEST_INVALID_BIND_FUNCTOR
454 IdleFunctor f;
455 handler.Bind(MyEventType, f);
456 #endif
457
458 // the handler can't be omitted when calling Bind()
459 #ifdef TEST_INVALID_BIND_NO_HANDLER
460 handler.Bind(MyEventType, &MyHandler::OnMyEvent);
461 #endif
462
463 // calling a derived class method with a base class pointer must not work
464 #ifdef TEST_INVALID_BIND_DERIVED
465 struct C1 : wxEvtHandler { };
466 struct C2 : wxEvtHandler { void OnWhatever(wxEvent&); };
467 C1 c1;
468 c1.Bind(&C2::OnWhatever);
469 #endif
470
471 // using object pointer incompatible with the method must not work
472 #ifdef TEST_INVALID_BIND_WRONG_CLASS
473 MySink mySink;
474 MyHandler myHandler;
475 myHandler.Bind(MyEventType, &MyHandler::OnMyEvent, &mySink);
476 #endif
477 }
478
479 #endif // wxHAS_EVENT_BIND