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