1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/events/propagation.cpp
3 // Purpose: Test events propagation
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
8 ///////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
23 #include "wx/scrolwin.h"
24 #include "wx/window.h"
29 #include "wx/scopedptr.h"
30 #include "wx/scopeguard.h"
35 // this string will record the execution of all handlers
39 wxDEFINE_EVENT(TEST_EVT
, wxCommandEvent
);
41 // a custom event handler tracing the propagation of the events of the
43 template <class Event
>
44 class TestEvtHandlerBase
: public wxEvtHandler
47 TestEvtHandlerBase(wxEventType evtType
, char tag
)
52 static_cast<wxEventFunction
>(&TestEvtHandlerBase::OnTest
));
55 // override ProcessEvent() to confirm that it is called for all event
56 // handlers in the chain
57 virtual bool ProcessEvent(wxEvent
& event
)
59 if ( event
.GetEventType() == m_evtType
)
60 g_str
+= 'o'; // "o" == "overridden"
62 return wxEvtHandler::ProcessEvent(event
);
66 void OnTest(wxEvent
& event
)
73 const wxEventType m_evtType
;
76 wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase
, Event
);
79 struct TestEvtHandler
: TestEvtHandlerBase
<wxCommandEvent
>
81 TestEvtHandler(char tag
)
82 : TestEvtHandlerBase
<wxCommandEvent
>(TEST_EVT
, tag
)
87 struct TestMenuEvtHandler
: TestEvtHandlerBase
<wxCommandEvent
>
89 TestMenuEvtHandler(char tag
)
90 : TestEvtHandlerBase
<wxCommandEvent
>(wxEVT_MENU
, tag
)
95 struct TestPaintEvtHandler
: TestEvtHandlerBase
<wxPaintEvent
>
97 TestPaintEvtHandler(char tag
)
98 : TestEvtHandlerBase
<wxPaintEvent
>(wxEVT_PAINT
, tag
)
103 // a window handling the test event
104 class TestWindow
: public wxWindow
107 TestWindow(wxWindow
*parent
, char tag
)
108 : wxWindow(parent
, wxID_ANY
),
111 Connect(TEST_EVT
, wxCommandEventHandler(TestWindow::OnTest
));
115 void OnTest(wxCommandEvent
& event
)
124 DECLARE_NO_COPY_CLASS(TestWindow
)
127 // a scroll window handling paint event: we want to have a special test case
128 // for this because the event propagation is complicated even further than
129 // usual here by the presence of wxScrollHelperEvtHandler in the event handlers
130 // chain and the fact that OnDraw() virtual method must be called if EVT_PAINT
132 class TestScrollWindow
: public wxScrolledWindow
135 TestScrollWindow(wxWindow
*parent
)
136 : wxScrolledWindow(parent
, wxID_ANY
)
138 Connect(wxEVT_PAINT
, wxPaintEventHandler(TestScrollWindow::OnPaint
));
141 virtual void OnDraw(wxDC
& WXUNUSED(dc
))
143 g_str
+= 'D'; // draw
147 void OnPaint(wxPaintEvent
& event
)
149 g_str
+= 'P'; // paint
153 wxDECLARE_NO_COPY_CLASS(TestScrollWindow
);
156 int DoFilterEvent(wxEvent
& event
)
158 if ( event
.GetEventType() == TEST_EVT
||
159 event
.GetEventType() == wxEVT_MENU
)
165 bool DoProcessEvent(wxEvent
& event
)
167 if ( event
.GetEventType() == TEST_EVT
||
168 event
.GetEventType() == wxEVT_MENU
)
174 } // anonymous namespace
176 // --------------------------------------------------------------------------
178 // --------------------------------------------------------------------------
180 class EventPropagationTestCase
: public CppUnit::TestCase
183 EventPropagationTestCase() {}
185 virtual void setUp();
186 virtual void tearDown();
189 CPPUNIT_TEST_SUITE( EventPropagationTestCase
);
190 CPPUNIT_TEST( OneHandler
);
191 CPPUNIT_TEST( TwoHandlers
);
192 CPPUNIT_TEST( WindowWithoutHandler
);
193 CPPUNIT_TEST( WindowWithHandler
);
194 CPPUNIT_TEST( ForwardEvent
);
195 CPPUNIT_TEST( ScrollWindowWithoutHandler
);
196 CPPUNIT_TEST( ScrollWindowWithHandler
);
197 CPPUNIT_TEST( MenuEvent
);
198 CPPUNIT_TEST_SUITE_END();
202 void WindowWithoutHandler();
203 void WindowWithHandler();
205 void ScrollWindowWithoutHandler();
206 void ScrollWindowWithHandler();
209 DECLARE_NO_COPY_CLASS(EventPropagationTestCase
)
212 // register in the unnamed registry so that these tests are run by default
213 CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase
);
215 // also include in its own registry so that these tests can be run alone
216 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EventPropagationTestCase
, "EventPropagationTestCase" );
218 void EventPropagationTestCase::setUp()
220 SetFilterEventFunc(DoFilterEvent
);
221 SetProcessEventFunc(DoProcessEvent
);
226 void EventPropagationTestCase::tearDown()
228 SetFilterEventFunc(NULL
);
229 SetProcessEventFunc(NULL
);
232 void EventPropagationTestCase::OneHandler()
234 wxCommandEvent
event(TEST_EVT
);
235 TestEvtHandler
h1('1');
236 h1
.ProcessEvent(event
);
237 CPPUNIT_ASSERT_EQUAL( "oa1A", g_str
);
240 void EventPropagationTestCase::TwoHandlers()
242 wxCommandEvent
event(TEST_EVT
);
243 TestEvtHandler
h1('1');
244 TestEvtHandler
h2('2');
245 h1
.SetNextHandler(&h2
);
246 h2
.SetPreviousHandler(&h1
);
247 h1
.ProcessEvent(event
);
248 CPPUNIT_ASSERT_EQUAL( "oa1o2A", g_str
);
251 void EventPropagationTestCase::WindowWithoutHandler()
253 wxCommandEvent
event(TEST_EVT
);
254 TestWindow
* const parent
= new TestWindow(wxTheApp
->GetTopWindow(), 'p');
255 wxON_BLOCK_EXIT_OBJ0( *parent
, wxWindow::Destroy
);
257 TestWindow
* const child
= new TestWindow(parent
, 'c');
259 child
->GetEventHandler()->ProcessEvent(event
);
260 CPPUNIT_ASSERT_EQUAL( "acpA", g_str
);
263 void EventPropagationTestCase::WindowWithHandler()
265 wxCommandEvent
event(TEST_EVT
);
266 TestWindow
* const parent
= new TestWindow(wxTheApp
->GetTopWindow(), 'p');
267 wxON_BLOCK_EXIT_OBJ0( *parent
, wxWindow::Destroy
);
269 TestWindow
* const child
= new TestWindow(parent
, 'c');
271 TestEvtHandler
h1('1');
272 child
->PushEventHandler(&h1
);
273 wxON_BLOCK_EXIT_OBJ1( *child
, wxWindow::PopEventHandler
, false );
274 TestEvtHandler
h2('2');
275 child
->PushEventHandler(&h2
);
276 wxON_BLOCK_EXIT_OBJ1( *child
, wxWindow::PopEventHandler
, false );
278 child
->HandleWindowEvent(event
);
279 CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str
);
282 void EventPropagationTestCase::ForwardEvent()
284 // The idea of this test is to check that the events explicitly forwarded
285 // to another event handler still get pre/post-processed as usual as this
286 // used to be broken by the fixes trying to avoid duplicate processing.
287 TestWindow
* const win
= new TestWindow(wxTheApp
->GetTopWindow(), 'w');
288 wxON_BLOCK_EXIT_OBJ0( *win
, wxWindow::Destroy
);
290 TestEvtHandler
h1('1');
291 win
->PushEventHandler(&h1
);
292 wxON_BLOCK_EXIT_OBJ1( *win
, wxWindow::PopEventHandler
, false );
294 class ForwardEvtHandler
: public wxEvtHandler
297 ForwardEvtHandler(wxEvtHandler
& h
) : m_h(&h
) { }
299 virtual bool ProcessEvent(wxEvent
& event
)
303 return m_h
->ProcessEvent(event
);
310 // First send the event directly to f.
311 wxCommandEvent
event1(TEST_EVT
);
312 f
.ProcessEvent(event1
);
313 CPPUNIT_ASSERT_EQUAL( "foa1wA", g_str
);
316 // And then also test sending it to f indirectly.
317 wxCommandEvent
event2(TEST_EVT
);
318 TestEvtHandler
h2('2');
319 h2
.SetNextHandler(&f
);
320 h2
.ProcessEvent(event2
);
321 CPPUNIT_ASSERT_EQUAL( "oa2fo1wAA", g_str
);
324 void EventPropagationTestCase::ScrollWindowWithoutHandler()
326 TestWindow
* const parent
= new TestWindow(wxTheApp
->GetTopWindow(), 'p');
327 wxON_BLOCK_EXIT_OBJ0( *parent
, wxWindow::Destroy
);
329 TestScrollWindow
* const win
= new TestScrollWindow(parent
);
331 #if !defined(__WXOSX__) && !defined(__WXGTK3__)
332 wxPaintEvent
event(win
->GetId());
333 win
->ProcessWindowEvent(event
);
334 CPPUNIT_ASSERT_EQUAL( "PD", g_str
);
337 wxCommandEvent
eventCmd(TEST_EVT
);
338 win
->HandleWindowEvent(eventCmd
);
339 CPPUNIT_ASSERT_EQUAL( "apA", g_str
);
342 void EventPropagationTestCase::ScrollWindowWithHandler()
344 TestWindow
* const parent
= new TestWindow(wxTheApp
->GetTopWindow(), 'p');
345 wxON_BLOCK_EXIT_OBJ0( *parent
, wxWindow::Destroy
);
347 TestScrollWindow
* const win
= new TestScrollWindow(parent
);
349 #if !defined(__WXOSX__) && !defined(__WXGTK3__)
350 TestPaintEvtHandler
h('h');
351 win
->PushEventHandler(&h
);
352 wxON_BLOCK_EXIT_OBJ1( *win
, wxWindow::PopEventHandler
, false );
354 wxPaintEvent
event(win
->GetId());
355 win
->ProcessWindowEvent(event
);
356 CPPUNIT_ASSERT_EQUAL( "ohPD", g_str
);
360 wxCommandEvent
eventCmd(TEST_EVT
);
361 win
->HandleWindowEvent(eventCmd
);
362 CPPUNIT_ASSERT_EQUAL( "apA", g_str
);
365 // Create a menu bar with a single menu containing wxID_APPLY menu item and
366 // attach it to the specified frame.
367 wxMenu
* CreateTestMenu(wxFrame
* frame
)
369 wxMenu
* const menu
= new wxMenu
;
370 menu
->Append(wxID_APPLY
);
371 wxMenuBar
* const mb
= new wxMenuBar
;
372 mb
->Append(menu
, "&Menu");
373 frame
->SetMenuBar(mb
);
378 // Helper for checking that the menu event processing resulted in the expected
379 // output from the handlers.
381 // Notice that this is supposed to be used with ASSERT_MENU_EVENT_RESULT()
382 // macro to make the file name and line number of the caller appear in the
385 CheckMenuEvent(wxMenu
* menu
, const char* result
, CppUnit::SourceLine sourceLine
)
389 // Trigger the menu event: this is more reliable than using
390 // wxUIActionSimulator and currently works in all ports as they all call
391 // wxMenuBase::SendEvent() from their respective menu event handlers.
392 menu
->SendEvent(wxID_APPLY
);
394 CPPUNIT_NS::assertEquals( result
, g_str
, sourceLine
, "" );
397 #define ASSERT_MENU_EVENT_RESULT(menu, result) \
398 CheckMenuEvent((menu), (result), CPPUNIT_SOURCELINE())
400 void EventPropagationTestCase::MenuEvent()
402 wxFrame
* const frame
= static_cast<wxFrame
*>(wxTheApp
->GetTopWindow());
404 // Create a minimal menu bar.
405 wxMenu
* const menu
= CreateTestMenu(frame
);
406 wxMenuBar
* const mb
= menu
->GetMenuBar();
407 wxScopedPtr
<wxMenuBar
> ensureMenuBarDestruction(mb
);
408 wxON_BLOCK_EXIT_OBJ1( *frame
, wxFrame::SetMenuBar
, (wxMenuBar
*)NULL
);
410 // Check that wxApp gets the event exactly once.
411 ASSERT_MENU_EVENT_RESULT( menu
, "aA" );
414 // Check that the menu event handler is called.
415 TestMenuEvtHandler
hm('m'); // 'm' for "menu"
416 menu
->SetNextHandler(&hm
);
417 wxON_BLOCK_EXIT_OBJ1( *menu
,
418 wxEvtHandler::SetNextHandler
, (wxEvtHandler
*)NULL
);
419 ASSERT_MENU_EVENT_RESULT( menu
, "aomA" );
422 // Test that the event handler associated with the menu bar gets the event.
423 TestMenuEvtHandler
hb('b'); // 'b' for "menu Bar"
424 mb
->PushEventHandler(&hb
);
425 wxON_BLOCK_EXIT_OBJ1( *mb
, wxWindow::PopEventHandler
, false );
427 ASSERT_MENU_EVENT_RESULT( menu
, "aomobA" );
430 // Also test that the window to which the menu belongs gets the event.
431 TestMenuEvtHandler
hw('w'); // 'w' for "Window"
432 frame
->PushEventHandler(&hw
);
433 wxON_BLOCK_EXIT_OBJ1( *frame
, wxWindow::PopEventHandler
, false );
435 ASSERT_MENU_EVENT_RESULT( menu
, "aomobowA" );