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