]> git.saurik.com Git - wxWidgets.git/blob - tests/events/evthandler.cpp
Test handling of events from the toolbar in an MDI parent frame.
[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 CPPUNIT_TEST( DisconnectWildcard );
164 CPPUNIT_TEST( AutoDisconnect );
165 #ifdef wxHAS_EVENT_BIND
166 CPPUNIT_TEST( BindFunction );
167 CPPUNIT_TEST( BindStaticMethod );
168 CPPUNIT_TEST( BindFunctor );
169 CPPUNIT_TEST( BindMethod );
170 CPPUNIT_TEST( BindMethodUsingBaseEvent );
171 CPPUNIT_TEST( BindFunctionUsingBaseEvent );
172 CPPUNIT_TEST( BindNonHandler );
173 CPPUNIT_TEST( InvalidBind );
174 #endif // wxHAS_EVENT_BIND
175 CPPUNIT_TEST_SUITE_END();
176
177 void BuiltinConnect();
178 void LegacyConnect();
179 void DisconnectWildcard();
180 void AutoDisconnect();
181 #ifdef wxHAS_EVENT_BIND
182 void BindFunction();
183 void BindStaticMethod();
184 void BindFunctor();
185 void BindMethod();
186 void BindMethodUsingBaseEvent();
187 void BindFunctionUsingBaseEvent();
188 void BindNonHandler();
189 void InvalidBind();
190 #endif // wxHAS_EVENT_BIND
191
192
193 // these member variables exceptionally don't use "m_" prefix because
194 // they're used so many times
195 MyHandler handler;
196 MyEvent e;
197
198 DECLARE_NO_COPY_CLASS(EvtHandlerTestCase)
199 };
200
201 // register in the unnamed registry so that these tests are run by default
202 CPPUNIT_TEST_SUITE_REGISTRATION( EvtHandlerTestCase );
203
204 // also include in its own registry so that these tests can be run alone
205 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtHandlerTestCase, "EvtHandlerTestCase" );
206
207 void EvtHandlerTestCase::BuiltinConnect()
208 {
209 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
210 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
211
212 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
213 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
214
215 // using casts like this is even uglier than using wxIdleEventHandler but
216 // it should still continue to work for compatibility
217 handler.Connect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
218 handler.Disconnect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
219
220 #ifdef wxHAS_EVENT_BIND
221 handler.Bind(wxEVT_IDLE, GlobalOnIdle);
222 handler.Unbind(wxEVT_IDLE, GlobalOnIdle);
223
224 IdleFunctor f;
225 handler.Bind(wxEVT_IDLE, f);
226 handler.Unbind(wxEVT_IDLE, f);
227
228 handler.Bind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
229 handler.Unbind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
230
231 handler.Bind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
232 handler.Unbind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
233 #endif // wxHAS_EVENT_BIND
234 }
235
236 void EvtHandlerTestCase::LegacyConnect()
237 {
238 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
239 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
240 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
241
242 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
243 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
244 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
245
246
247 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
248 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
249 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
250
251 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
252 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
253 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
254 }
255
256 void EvtHandlerTestCase::DisconnectWildcard()
257 {
258 // should be able to disconnect a different handler using "wildcard search"
259 MyHandler sink;
260 wxEvtHandler source;
261 source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink);
262 CPPUNIT_ASSERT(source.Disconnect(wxID_ANY, wxEVT_IDLE));
263 // destruction of source and sink here should properly clean up the
264 // wxEventConnectionRef without crashing
265 }
266
267 void EvtHandlerTestCase::AutoDisconnect()
268 {
269 wxEvtHandler source;
270 {
271 MyHandler sink;
272 source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink);
273 // mismatched event type, so nothing should be disconnected
274 CPPUNIT_ASSERT(!source.Disconnect(wxEVT_THREAD, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink));
275 }
276 // destruction of sink should have automatically disconnected it, so
277 // there should be nothing to disconnect anymore
278 CPPUNIT_ASSERT(!source.Disconnect(wxID_ANY, wxEVT_IDLE));
279 }
280
281 #ifdef wxHAS_EVENT_BIND
282
283 void EvtHandlerTestCase::BindFunction()
284 {
285 // function tests
286 handler.Bind( MyEventType, GlobalOnMyEvent );
287 g_called.Reset();
288 handler.ProcessEvent(e);
289 CPPUNIT_ASSERT( g_called.function );
290 handler.Unbind( MyEventType, GlobalOnMyEvent );
291 g_called.Reset();
292 handler.ProcessEvent(e);
293 CPPUNIT_ASSERT( !g_called.function ); // check that it was disconnected
294
295 handler.Bind( MyEventType, GlobalOnMyEvent, 0 );
296 handler.Unbind( MyEventType, GlobalOnMyEvent, 0 );
297
298 handler.Bind( MyEventType, GlobalOnMyEvent, 0, 0 );
299 handler.Unbind( MyEventType, GlobalOnMyEvent, 0, 0 );
300 }
301
302 void EvtHandlerTestCase::BindStaticMethod()
303 {
304 // static method tests (this is same as functions but still test it just in
305 // case we hit some strange compiler bugs)
306 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent );
307 g_called.Reset();
308 handler.ProcessEvent(e);
309 CPPUNIT_ASSERT( g_called.smethod );
310 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent );
311 g_called.Reset();
312 handler.ProcessEvent(e);
313 CPPUNIT_ASSERT( !g_called.smethod );
314
315 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
316 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
317
318 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
319 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
320 }
321
322 void EvtHandlerTestCase::BindFunctor()
323 {
324 // generalized functor tests
325 MyFunctor functor;
326
327 handler.Bind( MyEventType, functor );
328 g_called.Reset();
329 handler.ProcessEvent(e);
330 CPPUNIT_ASSERT( g_called.functor );
331 handler.Unbind( MyEventType, functor );
332 g_called.Reset();
333 handler.ProcessEvent(e);
334 CPPUNIT_ASSERT( !g_called.functor );
335
336 handler.Bind( MyEventType, functor, 0 );
337 handler.Unbind( MyEventType, functor, 0 );
338
339 handler.Bind( MyEventType, functor, 0, 0 );
340 handler.Unbind( MyEventType, functor, 0, 0 );
341
342 // test that a temporary functor is working as well and also test that
343 // unbinding a different (though equal) instance of the same functor does
344 // not work
345 MyFunctor func;
346 handler.Bind( MyEventType, MyFunctor() );
347 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, func ));
348
349 handler.Bind( MyEventType, MyFunctor(), 0 );
350 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, func, 0 ));
351
352 handler.Bind( MyEventType, MyFunctor(), 0, 0 );
353 CPPUNIT_ASSERT( !handler.Unbind( MyEventType, func, 0, 0 ));
354 }
355
356 void EvtHandlerTestCase::BindMethod()
357 {
358 // class method tests
359 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler );
360 g_called.Reset();
361 handler.ProcessEvent(e);
362 CPPUNIT_ASSERT( g_called.method );
363 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler );
364 g_called.Reset();
365 handler.ProcessEvent(e);
366 CPPUNIT_ASSERT( !g_called.method );
367
368 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
369 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
370
371 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
372 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
373 }
374
375 void EvtHandlerTestCase::BindMethodUsingBaseEvent()
376 {
377 // test connecting a method taking just wxEvent and not MyEvent: this
378 // should work too if we don't need any MyEvent-specific information in the
379 // handler
380 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler );
381 g_called.Reset();
382 handler.ProcessEvent(e);
383 CPPUNIT_ASSERT( g_called.method );
384 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler );
385 g_called.Reset();
386 handler.ProcessEvent(e);
387 CPPUNIT_ASSERT( !g_called.method );
388
389 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
390 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
391
392 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
393 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
394 }
395
396
397 void EvtHandlerTestCase::BindFunctionUsingBaseEvent()
398 {
399 // test connecting a function taking just wxEvent and not MyEvent: this
400 // should work too if we don't need any MyEvent-specific information in the
401 // handler
402 handler.Bind( MyEventType, GlobalOnEvent );
403 g_called.Reset();
404 handler.ProcessEvent(e);
405 CPPUNIT_ASSERT( g_called.function );
406 handler.Unbind( MyEventType, GlobalOnEvent );
407 g_called.Reset();
408 handler.ProcessEvent(e);
409 CPPUNIT_ASSERT( !g_called.function );
410
411 handler.Bind( MyEventType, GlobalOnEvent, 0 );
412 handler.Unbind( MyEventType, GlobalOnEvent, 0 );
413
414 handler.Bind( MyEventType, GlobalOnEvent, 0, 0 );
415 handler.Unbind( MyEventType, GlobalOnEvent, 0, 0 );
416 }
417
418
419
420 void EvtHandlerTestCase::BindNonHandler()
421 {
422 // class method tests for class not derived from wxEvtHandler
423 MySink sink;
424
425 handler.Bind( MyEventType, &MySink::OnMyEvent, &sink );
426 g_called.Reset();
427 handler.ProcessEvent(e);
428 CPPUNIT_ASSERT( g_called.method );
429 handler.Unbind( MyEventType, &MySink::OnMyEvent, &sink );
430 g_called.Reset();
431 handler.ProcessEvent(e);
432 CPPUNIT_ASSERT( !g_called.method );
433 }
434
435 void EvtHandlerTestCase::InvalidBind()
436 {
437 // these calls shouldn't compile but we unfortunately can't check this
438 // automatically, you need to uncomment them manually and test that
439 // compilation does indeed fail
440
441 // connecting a handler with incompatible signature shouldn't work
442 #ifdef TEST_INVALID_BIND_GLOBAL
443 handler.Bind(MyEventType, GlobalOnAnotherEvent);
444 #endif
445 #ifdef TEST_INVALID_BIND_STATIC
446 handler.Bind(MyEventType, &MyHandler::StaticOnAnotherEvent);
447 #endif
448 #ifdef TEST_INVALID_BIND_METHOD
449 handler.Bind(MyEventType, &MyHandler::OnAnotherEvent, &handler);
450 #endif
451 #ifdef TEST_INVALID_BIND_FUNCTOR
452 IdleFunctor f;
453 handler.Bind(MyEventType, f);
454 #endif
455
456 // the handler can't be omitted when calling Bind()
457 #ifdef TEST_INVALID_BIND_NO_HANDLER
458 handler.Bind(MyEventType, &MyHandler::OnMyEvent);
459 #endif
460
461 // calling a derived class method with a base class pointer must not work
462 #ifdef TEST_INVALID_BIND_DERIVED
463 struct C1 : wxEvtHandler { };
464 struct C2 : wxEvtHandler { void OnWhatever(wxEvent&); };
465 C1 c1;
466 c1.Bind(&C2::OnWhatever);
467 #endif
468
469 // using object pointer incompatible with the method must not work
470 #ifdef TEST_INVALID_BIND_WRONG_CLASS
471 MySink mySink;
472 MyHandler myHandler;
473 myHandler.Bind(MyEventType, &MyHandler::OnMyEvent, &mySink);
474 #endif
475 }
476
477 #endif // wxHAS_EVENT_BIND