allow passing temporary functors to Bind() too (closes #10653)
[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 void GlobalOnAnotherEvent(AnotherEvent&);
84
85 void GlobalOnIdle(wxIdleEvent&)
86 {
87 g_called.function = true;
88 }
89
90 struct MyFunctor
91 {
92 void operator()(MyEvent &) { g_called.functor = true; }
93 };
94
95 struct IdleFunctor
96 {
97 void operator()(wxIdleEvent &) { g_called.functor = true; }
98 };
99
100 class MyHandler : public wxEvtHandler
101 {
102 public:
103 static void StaticOnMyEvent(MyEvent &) { g_called.smethod = true; }
104 static void StaticOnAnotherEvent(AnotherEvent &);
105 static void StaticOnIdle(wxIdleEvent&) { g_called.smethod = true; }
106
107 void OnMyEvent(MyEvent&) { g_called.method = true; }
108 void OnEvent(wxEvent&) { g_called.method = true; }
109 void OnAnotherEvent(AnotherEvent&);
110 void OnIdle(wxIdleEvent&) { g_called.method = true; }
111 };
112
113 // we can also handle events in classes not deriving from wxEvtHandler
114 struct MySink
115 {
116 void OnMyEvent(MyEvent&) { g_called.method = true; }
117 void OnEvent(wxEvent&) { g_called.method = true; }
118 void OnIdle(wxIdleEvent&) { g_called.method = true; }
119 };
120
121 // also test event table compilation
122 class MyClassWithEventTable : public wxEvtHandler
123 {
124 public:
125 void OnMyEvent(MyEvent&) { g_called.method = true; }
126 void OnEvent(wxEvent&) { g_called.method = true; }
127 void OnAnotherEvent(AnotherEvent&);
128 void OnIdle(wxIdleEvent&) { g_called.method = true; }
129
130 private:
131 DECLARE_EVENT_TABLE()
132 };
133
134 BEGIN_EVENT_TABLE(MyClassWithEventTable, wxEvtHandler)
135 EVT_IDLE(MyClassWithEventTable::OnIdle)
136
137 EVT_MYEVENT(MyClassWithEventTable::OnMyEvent)
138 #ifdef wxHAS_EVENT_BIND
139 EVT_MYEVENT(MyClassWithEventTable::OnEvent)
140 #endif
141
142 // this shouldn't compile:
143 //EVT_MYEVENT(MyClassWithEventTable::OnIdle)
144 //EVT_IDLE(MyClassWithEventTable::OnAnotherEvent)
145 END_EVENT_TABLE()
146
147 } // anonymous namespace
148
149
150 // --------------------------------------------------------------------------
151 // test class
152 // --------------------------------------------------------------------------
153
154 class EvtHandlerTestCase : public CppUnit::TestCase
155 {
156 public:
157 EvtHandlerTestCase() {}
158
159 private:
160 CPPUNIT_TEST_SUITE( EvtHandlerTestCase );
161 CPPUNIT_TEST( BuiltinConnect );
162 CPPUNIT_TEST( LegacyConnect );
163 #ifdef wxHAS_EVENT_BIND
164 CPPUNIT_TEST( BindFunction );
165 CPPUNIT_TEST( BindStaticMethod );
166 CPPUNIT_TEST( BindFunctor );
167 CPPUNIT_TEST( BindMethod );
168 CPPUNIT_TEST( BindMethodUsingBaseEvent );
169 CPPUNIT_TEST( BindFunctionUsingBaseEvent );
170 CPPUNIT_TEST( BindNonHandler );
171 CPPUNIT_TEST( InvalidBind );
172 #endif // wxHAS_EVENT_BIND
173 CPPUNIT_TEST_SUITE_END();
174
175 void BuiltinConnect();
176 void LegacyConnect();
177 #ifdef wxHAS_EVENT_BIND
178 void BindFunction();
179 void BindStaticMethod();
180 void BindFunctor();
181 void BindMethod();
182 void BindMethodUsingBaseEvent();
183 void BindFunctionUsingBaseEvent();
184 void BindNonHandler();
185 void InvalidBind();
186 #endif // wxHAS_EVENT_BIND
187
188
189 // these member variables exceptionally don't use "m_" prefix because
190 // they're used so many times
191 MyHandler handler;
192 MyEvent e;
193
194 DECLARE_NO_COPY_CLASS(EvtHandlerTestCase)
195 };
196
197 // register in the unnamed registry so that these tests are run by default
198 CPPUNIT_TEST_SUITE_REGISTRATION( EvtHandlerTestCase );
199
200 // also include in it's own registry so that these tests can be run alone
201 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtHandlerTestCase, "EvtHandlerTestCase" );
202
203 void EvtHandlerTestCase::BuiltinConnect()
204 {
205 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
206 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
207
208 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
209 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
210
211 // using casts like this is even uglier than using wxIdleEventHandler but
212 // it should still continue to work for compatibility
213 handler.Connect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
214 handler.Disconnect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
215
216 #ifdef wxHAS_EVENT_BIND
217 handler.Bind(wxEVT_IDLE, GlobalOnIdle);
218 handler.Unbind(wxEVT_IDLE, GlobalOnIdle);
219
220 IdleFunctor f;
221 handler.Bind(wxEVT_IDLE, f);
222 handler.Unbind(wxEVT_IDLE, f);
223
224 handler.Bind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
225 handler.Unbind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
226
227 handler.Bind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
228 handler.Unbind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
229 #endif // wxHAS_EVENT_BIND
230 }
231
232 void EvtHandlerTestCase::LegacyConnect()
233 {
234 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
235 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
236 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
237
238 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
239 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
240 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
241
242
243 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
244 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
245 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
246
247 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
248 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
249 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
250 }
251
252 #ifdef wxHAS_EVENT_BIND
253
254 void EvtHandlerTestCase::BindFunction()
255 {
256 // function tests
257 handler.Bind( MyEventType, GlobalOnMyEvent );
258 g_called.Reset();
259 handler.ProcessEvent(e);
260 CPPUNIT_ASSERT( g_called.function );
261 handler.Unbind( MyEventType, GlobalOnMyEvent );
262 g_called.Reset();
263 handler.ProcessEvent(e);
264 CPPUNIT_ASSERT( !g_called.function ); // check that it was disconnected
265
266 handler.Bind( MyEventType, GlobalOnMyEvent, 0 );
267 handler.Unbind( MyEventType, GlobalOnMyEvent, 0 );
268
269 handler.Bind( MyEventType, GlobalOnMyEvent, 0, 0 );
270 handler.Unbind( MyEventType, GlobalOnMyEvent, 0, 0 );
271 }
272
273 void EvtHandlerTestCase::BindStaticMethod()
274 {
275 // static method tests (this is same as functions but still test it just in
276 // case we hit some strange compiler bugs)
277 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent );
278 g_called.Reset();
279 handler.ProcessEvent(e);
280 CPPUNIT_ASSERT( g_called.smethod );
281 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent );
282 g_called.Reset();
283 handler.ProcessEvent(e);
284 CPPUNIT_ASSERT( !g_called.smethod );
285
286 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
287 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
288
289 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
290 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
291 }
292
293 void EvtHandlerTestCase::BindFunctor()
294 {
295 // generalized functor tests
296 MyFunctor functor;
297
298 handler.Bind( MyEventType, functor );
299 g_called.Reset();
300 handler.ProcessEvent(e);
301 CPPUNIT_ASSERT( g_called.functor );
302 handler.Unbind( MyEventType, functor );
303 g_called.Reset();
304 handler.ProcessEvent(e);
305 CPPUNIT_ASSERT( !g_called.functor );
306
307 handler.Bind( MyEventType, functor, 0 );
308 handler.Unbind( MyEventType, functor, 0 );
309
310 handler.Bind( MyEventType, functor, 0, 0 );
311 handler.Unbind( MyEventType, functor, 0, 0 );
312
313 // Test that a temporary functor is working as well. Note that Unbind will
314 // fail because a functor can only be unbound if it is the same instance as
315 // in Bind!
316
317 handler.Bind( MyEventType, MyFunctor() );
318 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, MyFunctor() ));
319
320 handler.Bind( MyEventType, MyFunctor(), 0 );
321 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, MyFunctor(), 0 ));
322
323 handler.Bind( MyEventType, MyFunctor(), 0, 0 );
324 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, MyFunctor(), 0, 0 ));
325 }
326
327 void EvtHandlerTestCase::BindMethod()
328 {
329 // class method tests
330 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler );
331 g_called.Reset();
332 handler.ProcessEvent(e);
333 CPPUNIT_ASSERT( g_called.method );
334 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler );
335 g_called.Reset();
336 handler.ProcessEvent(e);
337 CPPUNIT_ASSERT( !g_called.method );
338
339 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
340 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
341
342 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
343 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
344 }
345
346 void EvtHandlerTestCase::BindMethodUsingBaseEvent()
347 {
348 // test connecting a method taking just wxEvent and not MyEvent: this
349 // should work too if we don't need any MyEvent-specific information in the
350 // handler
351 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler );
352 g_called.Reset();
353 handler.ProcessEvent(e);
354 CPPUNIT_ASSERT( g_called.method );
355 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler );
356 g_called.Reset();
357 handler.ProcessEvent(e);
358 CPPUNIT_ASSERT( !g_called.method );
359
360 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
361 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
362
363 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
364 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
365 }
366
367
368 void EvtHandlerTestCase::BindFunctionUsingBaseEvent()
369 {
370 // test connecting a function taking just wxEvent and not MyEvent: this
371 // should work too if we don't need any MyEvent-specific information in the
372 // handler
373 handler.Bind( MyEventType, GlobalOnEvent );
374 g_called.Reset();
375 handler.ProcessEvent(e);
376 CPPUNIT_ASSERT( g_called.function );
377 handler.Unbind( MyEventType, GlobalOnEvent );
378 g_called.Reset();
379 handler.ProcessEvent(e);
380 CPPUNIT_ASSERT( !g_called.function );
381
382 handler.Bind( MyEventType, GlobalOnEvent, 0 );
383 handler.Unbind( MyEventType, GlobalOnEvent, 0 );
384
385 handler.Bind( MyEventType, GlobalOnEvent, 0, 0 );
386 handler.Unbind( MyEventType, GlobalOnEvent, 0, 0 );
387 }
388
389
390
391 void EvtHandlerTestCase::BindNonHandler()
392 {
393 // class method tests for class not derived from wxEvtHandler
394 MySink sink;
395
396 handler.Bind( MyEventType, &MySink::OnMyEvent, &sink );
397 g_called.Reset();
398 handler.ProcessEvent(e);
399 CPPUNIT_ASSERT( g_called.method );
400 handler.Unbind( MyEventType, &MySink::OnMyEvent, &sink );
401 g_called.Reset();
402 handler.ProcessEvent(e);
403 CPPUNIT_ASSERT( !g_called.method );
404 }
405
406 void EvtHandlerTestCase::InvalidBind()
407 {
408 // these calls shouldn't compile but we unfortunately can't check this
409 // automatically, you need to uncomment them manually and test that
410 // compilation does indeed fail
411
412 // connecting a handler with incompatible signature shouldn't work
413 #ifdef TEST_INVALID_BIND_GLOBAL
414 handler.Bind(MyEventType, GlobalOnAnotherEvent);
415 #endif
416 #ifdef TEST_INVALID_BIND_STATIC
417 handler.Bind(MyEventType, &MyHandler::StaticOnAnotherEvent);
418 #endif
419 #ifdef TEST_INVALID_BIND_METHOD
420 handler.Bind(MyEventType, &MyHandler::OnAnotherEvent, &handler);
421 #endif
422 #ifdef TEST_INVALID_BIND_FUNCTOR
423 IdleFunctor f;
424 handler.Bind(MyEventType, f);
425 #endif
426
427 // the handler can't be omitted when calling Bind()
428 #ifdef TEST_INVALID_BIND_NO_HANDLER
429 handler.Bind(MyEventType, &MyHandler::OnMyEvent);
430 #endif
431
432 // calling a derived class method with a base class pointer must not work
433 #ifdef TEST_INVALID_BIND_DERIVED
434 struct C1 : wxEvtHandler { };
435 struct C2 : wxEvtHandler { void OnWhatever(wxEvent&); };
436 C1 c1;
437 c1.Bind(&C2::OnWhatever);
438 #endif
439
440 // using object pointer incompatible with the method must not work
441 #ifdef TEST_INVALID_BIND_WRONG_CLASS
442 MySink mySink;
443 MyHandler myHandler;
444 myHandler.Bind(MyEventType, &MyHandler::OnMyEvent, &mySink);
445 #endif
446 }
447
448 #endif // wxHAS_EVENT_BIND