propagation.cpp 18 KB

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