]> git.saurik.com Git - wxWidgets.git/blame - tests/events/propagation.cpp
Recognize "sandbox" user as indicating automatic testing.
[wxWidgets.git] / tests / events / propagation.cpp
CommitLineData
1649d288
VZ
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
c21dcf80
VZ
21 #include "wx/app.h"
22 #include "wx/event.h"
ce45133e 23 #include "wx/scrolwin.h"
c21dcf80 24 #include "wx/window.h"
1649d288
VZ
25#endif // WX_PRECOMP
26
a7c0de8a 27#include "wx/docmdi.h"
3bad8c39
VZ
28#include "wx/frame.h"
29#include "wx/menu.h"
bbde2164 30#include "wx/scopedptr.h"
1649d288
VZ
31#include "wx/scopeguard.h"
32
23366691
VZ
33// FIXME: Currently under OS X testing paint event doesn't work because neither
34// calling Refresh()+Update() nor even sending wxPaintEvent directly to
35// the window doesn't result in calls to its event handlers, so disable
36// some tests there. But this should be fixed and the tests reenabled
37// because wxPaintEvent propagation in wxScrolledWindow is a perfect
38// example of fragile code that could be broken under OS X.
39#ifndef __WXOSX__
40 #define CAN_TEST_PAINT_EVENTS
41#endif
42
1649d288
VZ
43namespace
44{
45
46// this string will record the execution of all handlers
47wxString g_str;
48
49// a custom event
50wxDEFINE_EVENT(TEST_EVT, wxCommandEvent);
51
ce45133e
VZ
52// a custom event handler tracing the propagation of the events of the
53// specified types
54template <class Event>
55class TestEvtHandlerBase : public wxEvtHandler
1649d288
VZ
56{
57public:
ce45133e
VZ
58 TestEvtHandlerBase(wxEventType evtType, char tag)
59 : m_evtType(evtType),
60 m_tag(tag)
1649d288 61 {
ce45133e
VZ
62 Connect(evtType,
63 static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest));
1649d288
VZ
64 }
65
66 // override ProcessEvent() to confirm that it is called for all event
67 // handlers in the chain
68 virtual bool ProcessEvent(wxEvent& event)
69 {
ce45133e 70 if ( event.GetEventType() == m_evtType )
1649d288
VZ
71 g_str += 'o'; // "o" == "overridden"
72
73 return wxEvtHandler::ProcessEvent(event);
74 }
75
76private:
ce45133e 77 void OnTest(wxEvent& event)
1649d288
VZ
78 {
79 g_str += m_tag;
80
81 event.Skip();
82 }
83
ce45133e 84 const wxEventType m_evtType;
1649d288
VZ
85 const char m_tag;
86
ce45133e
VZ
87 wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase, Event);
88};
89
90struct TestEvtHandler : TestEvtHandlerBase<wxCommandEvent>
91{
92 TestEvtHandler(char tag)
93 : TestEvtHandlerBase<wxCommandEvent>(TEST_EVT, tag)
94 {
95 }
96};
97
3bad8c39
VZ
98struct TestMenuEvtHandler : TestEvtHandlerBase<wxCommandEvent>
99{
100 TestMenuEvtHandler(char tag)
101 : TestEvtHandlerBase<wxCommandEvent>(wxEVT_MENU, tag)
102 {
103 }
104};
105
ce45133e
VZ
106struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
107{
108 TestPaintEvtHandler(char tag)
109 : TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag)
110 {
111 }
1649d288
VZ
112};
113
a7c0de8a
VZ
114// Another custom event handler, suitable for use with Connect().
115struct TestEvtSink : wxEvtHandler
116{
117 TestEvtSink(char tag)
118 : m_tag(tag)
119 {
120 }
121
122 void Handle(wxEvent& event)
123 {
124 g_str += m_tag;
125
126 event.Skip();
127 }
128
129 const char m_tag;
130
131 wxDECLARE_NO_COPY_CLASS(TestEvtSink);
132};
133
1649d288
VZ
134// a window handling the test event
135class TestWindow : public wxWindow
136{
137public:
138 TestWindow(wxWindow *parent, char tag)
139 : wxWindow(parent, wxID_ANY),
140 m_tag(tag)
141 {
142 Connect(TEST_EVT, wxCommandEventHandler(TestWindow::OnTest));
143 }
144
145private:
146 void OnTest(wxCommandEvent& event)
147 {
148 g_str += m_tag;
149
150 event.Skip();
151 }
152
153 const char m_tag;
154
155 DECLARE_NO_COPY_CLASS(TestWindow)
156};
157
ce45133e
VZ
158// a scroll window handling paint event: we want to have a special test case
159// for this because the event propagation is complicated even further than
160// usual here by the presence of wxScrollHelperEvtHandler in the event handlers
161// chain and the fact that OnDraw() virtual method must be called if EVT_PAINT
162// is not handled
163class TestScrollWindow : public wxScrolledWindow
164{
165public:
166 TestScrollWindow(wxWindow *parent)
167 : wxScrolledWindow(parent, wxID_ANY)
168 {
169 Connect(wxEVT_PAINT, wxPaintEventHandler(TestScrollWindow::OnPaint));
170 }
171
23366691
VZ
172 void GeneratePaintEvent()
173 {
174#ifdef __WXGTK__
175 // We need to map the window, otherwise we're not going to get any
176 // paint events for it.
177 wxYield();
178
179 // Ignore events generated during the initial mapping.
180 g_str.clear();
181#endif // __WXGTK__
182
183 Refresh();
184 Update();
185 }
186
ce45133e
VZ
187 virtual void OnDraw(wxDC& WXUNUSED(dc))
188 {
189 g_str += 'D'; // draw
190 }
191
192private:
193 void OnPaint(wxPaintEvent& event)
194 {
195 g_str += 'P'; // paint
196 event.Skip();
197 }
198
199 wxDECLARE_NO_COPY_CLASS(TestScrollWindow);
200};
201
1649d288
VZ
202int DoFilterEvent(wxEvent& event)
203{
3bad8c39
VZ
204 if ( event.GetEventType() == TEST_EVT ||
205 event.GetEventType() == wxEVT_MENU )
1649d288
VZ
206 g_str += 'a';
207
208 return -1;
209}
210
211bool DoProcessEvent(wxEvent& event)
212{
3bad8c39
VZ
213 if ( event.GetEventType() == TEST_EVT ||
214 event.GetEventType() == wxEVT_MENU )
1649d288
VZ
215 g_str += 'A';
216
217 return false;
218}
219
220} // anonymous namespace
221
222// --------------------------------------------------------------------------
223// test class
224// --------------------------------------------------------------------------
225
226class EventPropagationTestCase : public CppUnit::TestCase
227{
228public:
229 EventPropagationTestCase() {}
230
231 virtual void setUp();
232 virtual void tearDown();
233
234private:
235 CPPUNIT_TEST_SUITE( EventPropagationTestCase );
236 CPPUNIT_TEST( OneHandler );
237 CPPUNIT_TEST( TwoHandlers );
238 CPPUNIT_TEST( WindowWithoutHandler );
239 CPPUNIT_TEST( WindowWithHandler );
bbdee10d 240 CPPUNIT_TEST( ForwardEvent );
ce45133e
VZ
241 CPPUNIT_TEST( ScrollWindowWithoutHandler );
242 CPPUNIT_TEST( ScrollWindowWithHandler );
3bad8c39 243 CPPUNIT_TEST( MenuEvent );
a7c0de8a 244 CPPUNIT_TEST( DocView );
1649d288
VZ
245 CPPUNIT_TEST_SUITE_END();
246
247 void OneHandler();
248 void TwoHandlers();
249 void WindowWithoutHandler();
250 void WindowWithHandler();
bbdee10d 251 void ForwardEvent();
ce45133e
VZ
252 void ScrollWindowWithoutHandler();
253 void ScrollWindowWithHandler();
3bad8c39 254 void MenuEvent();
a7c0de8a 255 void DocView();
1649d288
VZ
256
257 DECLARE_NO_COPY_CLASS(EventPropagationTestCase)
258};
259
260// register in the unnamed registry so that these tests are run by default
261CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase );
262
e3778b4d 263// also include in its own registry so that these tests can be run alone
1649d288
VZ
264CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EventPropagationTestCase, "EventPropagationTestCase" );
265
266void EventPropagationTestCase::setUp()
267{
268 SetFilterEventFunc(DoFilterEvent);
269 SetProcessEventFunc(DoProcessEvent);
270
271 g_str.clear();
272}
273
274void EventPropagationTestCase::tearDown()
275{
276 SetFilterEventFunc(NULL);
277 SetProcessEventFunc(NULL);
278}
279
280void EventPropagationTestCase::OneHandler()
281{
282 wxCommandEvent event(TEST_EVT);
283 TestEvtHandler h1('1');
284 h1.ProcessEvent(event);
285 CPPUNIT_ASSERT_EQUAL( "oa1A", g_str );
286}
287
288void EventPropagationTestCase::TwoHandlers()
289{
290 wxCommandEvent event(TEST_EVT);
291 TestEvtHandler h1('1');
292 TestEvtHandler h2('2');
293 h1.SetNextHandler(&h2);
294 h2.SetPreviousHandler(&h1);
295 h1.ProcessEvent(event);
296 CPPUNIT_ASSERT_EQUAL( "oa1o2A", g_str );
297}
298
299void EventPropagationTestCase::WindowWithoutHandler()
300{
301 wxCommandEvent event(TEST_EVT);
302 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
303 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
304
305 TestWindow * const child = new TestWindow(parent, 'c');
306
004867db 307 child->GetEventHandler()->ProcessEvent(event);
1649d288
VZ
308 CPPUNIT_ASSERT_EQUAL( "acpA", g_str );
309}
310
311void EventPropagationTestCase::WindowWithHandler()
312{
313 wxCommandEvent event(TEST_EVT);
314 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
315 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
316
317 TestWindow * const child = new TestWindow(parent, 'c');
318
319 TestEvtHandler h1('1');
320 child->PushEventHandler(&h1);
9ebfc963 321 wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
1649d288
VZ
322 TestEvtHandler h2('2');
323 child->PushEventHandler(&h2);
9ebfc963 324 wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
1649d288
VZ
325
326 child->HandleWindowEvent(event);
327 CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str );
328}
329
bbdee10d
VZ
330void EventPropagationTestCase::ForwardEvent()
331{
332 // The idea of this test is to check that the events explicitly forwarded
333 // to another event handler still get pre/post-processed as usual as this
334 // used to be broken by the fixes trying to avoid duplicate processing.
335 TestWindow * const win = new TestWindow(wxTheApp->GetTopWindow(), 'w');
336 wxON_BLOCK_EXIT_OBJ0( *win, wxWindow::Destroy );
337
338 TestEvtHandler h1('1');
339 win->PushEventHandler(&h1);
340 wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
341
342 class ForwardEvtHandler : public wxEvtHandler
343 {
344 public:
345 ForwardEvtHandler(wxEvtHandler& h) : m_h(&h) { }
346
347 virtual bool ProcessEvent(wxEvent& event)
348 {
349 g_str += 'f';
350
351 return m_h->ProcessEvent(event);
352 }
353
354 private:
355 wxEvtHandler *m_h;
356 } f(h1);
357
358 // First send the event directly to f.
359 wxCommandEvent event1(TEST_EVT);
360 f.ProcessEvent(event1);
361 CPPUNIT_ASSERT_EQUAL( "foa1wA", g_str );
362 g_str.clear();
363
364 // And then also test sending it to f indirectly.
365 wxCommandEvent event2(TEST_EVT);
366 TestEvtHandler h2('2');
367 h2.SetNextHandler(&f);
368 h2.ProcessEvent(event2);
369 CPPUNIT_ASSERT_EQUAL( "oa2fo1wAA", g_str );
370}
371
ce45133e
VZ
372void EventPropagationTestCase::ScrollWindowWithoutHandler()
373{
52212bcb
VZ
374 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
375 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
376
377 TestScrollWindow * const win = new TestScrollWindow(parent);
ce45133e 378
23366691
VZ
379#ifdef CAN_TEST_PAINT_EVENTS
380 win->GeneratePaintEvent();
ce45133e 381 CPPUNIT_ASSERT_EQUAL( "PD", g_str );
f009da6e 382#endif
23366691 383
52212bcb
VZ
384 g_str.clear();
385 wxCommandEvent eventCmd(TEST_EVT);
386 win->HandleWindowEvent(eventCmd);
387 CPPUNIT_ASSERT_EQUAL( "apA", g_str );
ce45133e
VZ
388}
389
390void EventPropagationTestCase::ScrollWindowWithHandler()
391{
52212bcb
VZ
392 TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
393 wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
394
395 TestScrollWindow * const win = new TestScrollWindow(parent);
ce45133e 396
23366691 397#ifdef CAN_TEST_PAINT_EVENTS
ce45133e
VZ
398 TestPaintEvtHandler h('h');
399 win->PushEventHandler(&h);
400 wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
401
23366691 402 win->GeneratePaintEvent();
ce45133e 403 CPPUNIT_ASSERT_EQUAL( "ohPD", g_str );
15536294 404#endif
52212bcb
VZ
405
406 g_str.clear();
407 wxCommandEvent eventCmd(TEST_EVT);
408 win->HandleWindowEvent(eventCmd);
409 CPPUNIT_ASSERT_EQUAL( "apA", g_str );
ce45133e
VZ
410}
411
bbde2164
VZ
412// Create a menu bar with a single menu containing wxID_APPLY menu item and
413// attach it to the specified frame.
414wxMenu* CreateTestMenu(wxFrame* frame)
415{
416 wxMenu* const menu = new wxMenu;
417 menu->Append(wxID_APPLY);
418 wxMenuBar* const mb = new wxMenuBar;
419 mb->Append(menu, "&Menu");
420 frame->SetMenuBar(mb);
421
422 return menu;
423}
424
3bad8c39
VZ
425// Helper for checking that the menu event processing resulted in the expected
426// output from the handlers.
9fe94219
VZ
427//
428// Notice that this is supposed to be used with ASSERT_MENU_EVENT_RESULT()
429// macro to make the file name and line number of the caller appear in the
430// failure messages.
431void
432CheckMenuEvent(wxMenu* menu, const char* result, CppUnit::SourceLine sourceLine)
3bad8c39
VZ
433{
434 g_str.clear();
435
436 // Trigger the menu event: this is more reliable than using
437 // wxUIActionSimulator and currently works in all ports as they all call
438 // wxMenuBase::SendEvent() from their respective menu event handlers.
bbde2164 439 menu->SendEvent(wxID_APPLY);
3bad8c39 440
9fe94219 441 CPPUNIT_NS::assertEquals( result, g_str, sourceLine, "" );
3bad8c39
VZ
442}
443
9fe94219
VZ
444#define ASSERT_MENU_EVENT_RESULT(menu, result) \
445 CheckMenuEvent((menu), (result), CPPUNIT_SOURCELINE())
446
3bad8c39
VZ
447void EventPropagationTestCase::MenuEvent()
448{
3bad8c39 449 wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow());
bbde2164
VZ
450
451 // Create a minimal menu bar.
452 wxMenu* const menu = CreateTestMenu(frame);
453 wxMenuBar* const mb = menu->GetMenuBar();
454 wxScopedPtr<wxMenuBar> ensureMenuBarDestruction(mb);
3bad8c39
VZ
455 wxON_BLOCK_EXIT_OBJ1( *frame, wxFrame::SetMenuBar, (wxMenuBar*)NULL );
456
457 // Check that wxApp gets the event exactly once.
9fe94219 458 ASSERT_MENU_EVENT_RESULT( menu, "aA" );
3bad8c39
VZ
459
460
461 // Check that the menu event handler is called.
462 TestMenuEvtHandler hm('m'); // 'm' for "menu"
463 menu->SetNextHandler(&hm);
464 wxON_BLOCK_EXIT_OBJ1( *menu,
465 wxEvtHandler::SetNextHandler, (wxEvtHandler*)NULL );
9fe94219 466 ASSERT_MENU_EVENT_RESULT( menu, "aomA" );
3bad8c39
VZ
467
468
4ed3f4ab
VZ
469 // Test that the event handler associated with the menu bar gets the event.
470 TestMenuEvtHandler hb('b'); // 'b' for "menu Bar"
471 mb->PushEventHandler(&hb);
472 wxON_BLOCK_EXIT_OBJ1( *mb, wxWindow::PopEventHandler, false );
473
9fe94219 474 ASSERT_MENU_EVENT_RESULT( menu, "aomobA" );
4ed3f4ab
VZ
475
476
3bad8c39
VZ
477 // Also test that the window to which the menu belongs gets the event.
478 TestMenuEvtHandler hw('w'); // 'w' for "Window"
479 frame->PushEventHandler(&hw);
480 wxON_BLOCK_EXIT_OBJ1( *frame, wxWindow::PopEventHandler, false );
481
9fe94219 482 ASSERT_MENU_EVENT_RESULT( menu, "aomobowA" );
3bad8c39 483}
a7c0de8a
VZ
484
485// Minimal viable implementations of wxDocument and wxView.
486class EventTestDocument : public wxDocument
487{
488public:
489 EventTestDocument() { }
490
491 wxDECLARE_DYNAMIC_CLASS(EventTestDocument);
492};
493
494class EventTestView : public wxView
495{
496public:
497 EventTestView() { }
498
499 virtual void OnDraw(wxDC*) { }
500
501 wxDECLARE_DYNAMIC_CLASS(EventTestView);
502};
503
504wxIMPLEMENT_DYNAMIC_CLASS(EventTestDocument, wxDocument);
505wxIMPLEMENT_DYNAMIC_CLASS(EventTestView, wxView);
506
507void EventPropagationTestCase::DocView()
508{
509 // Set up the parent frame and its menu bar.
510 wxDocManager docManager;
511
512 wxScopedPtr<wxDocMDIParentFrame>
513 parent(new wxDocMDIParentFrame(&docManager, NULL, wxID_ANY, "Parent"));
514
515 wxMenu* const menu = CreateTestMenu(parent.get());
516
517
518 // Set up the event handlers.
519 TestEvtSink sinkDM('m');
520 docManager.Connect(wxEVT_MENU,
521 wxEventHandler(TestEvtSink::Handle), NULL, &sinkDM);
522
523 TestEvtSink sinkParent('p');
524 parent->Connect(wxEVT_MENU,
525 wxEventHandler(TestEvtSink::Handle), NULL, &sinkParent);
526
527
528 // Check that wxDocManager and wxFrame get the event in order.
529 ASSERT_MENU_EVENT_RESULT( menu, "ampA" );
530
531
532 // Now check what happens if we have an active document.
533 wxDocTemplate docTemplate(&docManager, "Test", "", "", "",
534 "Test Document", "Test View",
535 wxCLASSINFO(EventTestDocument),
536 wxCLASSINFO(EventTestView));
537 wxDocument* const doc = docTemplate.CreateDocument("");
538 wxView* const view = doc->GetFirstView();
539
540 wxScopedPtr<wxFrame>
541 child(new wxDocMDIChildFrame(doc, view, parent.get(), wxID_ANY, "Child"));
542
543 wxMenu* const menuChild = CreateTestMenu(child.get());
544
545#ifdef __WXGTK__
546 // There are a lot of hacks related to child frame menu bar handling in
547 // wxGTK and, in particular, the code in src/gtk/mdi.cpp relies on getting
548 // idle events to really put everything in place. Moreover, as wxGTK uses
549 // GtkNotebook as its MDI pages container, the frame must be shown for all
550 // this to work as gtk_notebook_set_current_page() doesn't do anything if
551 // called for a hidden window (this incredible fact cost me quite some time
552 // to find empirically -- only to notice its confirmation in GTK+
553 // documentation immediately afterwards). So just do whatever it takes to
554 // make things work "as usual".
555 child->Show();
556 parent->Show();
557 wxYield();
558#endif // __WXGTK__
559
560 TestEvtSink sinkDoc('d');
561 doc->Connect(wxEVT_MENU,
562 wxEventHandler(TestEvtSink::Handle), NULL, &sinkDoc);
563
564 TestEvtSink sinkView('v');
565 view->Connect(wxEVT_MENU,
566 wxEventHandler(TestEvtSink::Handle), NULL, &sinkView);
567
568 TestEvtSink sinkChild('c');
569 child->Connect(wxEVT_MENU,
570 wxEventHandler(TestEvtSink::Handle), NULL, &sinkChild);
571
572 // Check that wxDocument, wxView, wxDocManager, child frame and the parent
573 // get the event in order.
574 ASSERT_MENU_EVENT_RESULT( menuChild, "advmcpA" );
575}