| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- ///////////////////////////////////////////////////////////////////////////////
- // Name: tests/events/propagation.cpp
- // Purpose: Test events propagation
- // Author: Vadim Zeitlin
- // Created: 2009-01-16
- // Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
- ///////////////////////////////////////////////////////////////////////////////
- // ----------------------------------------------------------------------------
- // headers
- // ----------------------------------------------------------------------------
- #include "testprec.h"
- #ifdef __BORLANDC__
- #pragma hdrstop
- #endif
- #ifndef WX_PRECOMP
- #include "wx/app.h"
- #include "wx/event.h"
- #include "wx/scrolwin.h"
- #include "wx/window.h"
- #endif // WX_PRECOMP
- #include "wx/docmdi.h"
- #include "wx/frame.h"
- #include "wx/menu.h"
- #include "wx/scopedptr.h"
- #include "wx/scopeguard.h"
- #include "wx/toolbar.h"
- #include "wx/uiaction.h"
- // FIXME: Currently under OS X testing paint event doesn't work because neither
- // calling Refresh()+Update() nor even sending wxPaintEvent directly to
- // the window doesn't result in calls to its event handlers, so disable
- // some tests there. But this should be fixed and the tests reenabled
- // because wxPaintEvent propagation in wxScrolledWindow is a perfect
- // example of fragile code that could be broken under OS X.
- #ifndef __WXOSX__
- #define CAN_TEST_PAINT_EVENTS
- #endif
- namespace
- {
- // this string will record the execution of all handlers
- wxString g_str;
- // a custom event
- wxDEFINE_EVENT(TEST_EVT, wxCommandEvent);
- // a custom event handler tracing the propagation of the events of the
- // specified types
- template <class Event>
- class TestEvtHandlerBase : public wxEvtHandler
- {
- public:
- TestEvtHandlerBase(wxEventType evtType, char tag)
- : m_evtType(evtType),
- m_tag(tag)
- {
- Connect(evtType,
- static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest));
- }
- // override ProcessEvent() to confirm that it is called for all event
- // handlers in the chain
- virtual bool ProcessEvent(wxEvent& event)
- {
- if ( event.GetEventType() == m_evtType )
- g_str += 'o'; // "o" == "overridden"
- return wxEvtHandler::ProcessEvent(event);
- }
- private:
- void OnTest(wxEvent& event)
- {
- g_str += m_tag;
- event.Skip();
- }
- const wxEventType m_evtType;
- const char m_tag;
- wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase, Event);
- };
- struct TestEvtHandler : TestEvtHandlerBase<wxCommandEvent>
- {
- TestEvtHandler(char tag)
- : TestEvtHandlerBase<wxCommandEvent>(TEST_EVT, tag)
- {
- }
- };
- struct TestMenuEvtHandler : TestEvtHandlerBase<wxCommandEvent>
- {
- TestMenuEvtHandler(char tag)
- : TestEvtHandlerBase<wxCommandEvent>(wxEVT_MENU, tag)
- {
- }
- };
- struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
- {
- TestPaintEvtHandler(char tag)
- : TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag)
- {
- }
- };
- // Another custom event handler, suitable for use with Connect().
- struct TestEvtSink : wxEvtHandler
- {
- TestEvtSink(char tag)
- : m_tag(tag)
- {
- }
- void Handle(wxEvent& event)
- {
- g_str += m_tag;
- event.Skip();
- }
- const char m_tag;
- wxDECLARE_NO_COPY_CLASS(TestEvtSink);
- };
- // a window handling the test event
- class TestWindow : public wxWindow
- {
- public:
- TestWindow(wxWindow *parent, char tag)
- : wxWindow(parent, wxID_ANY),
- m_tag(tag)
- {
- Connect(TEST_EVT, wxCommandEventHandler(TestWindow::OnTest));
- }
- private:
- void OnTest(wxCommandEvent& event)
- {
- g_str += m_tag;
- event.Skip();
- }
- const char m_tag;
- DECLARE_NO_COPY_CLASS(TestWindow)
- };
- // a scroll window handling paint event: we want to have a special test case
- // for this because the event propagation is complicated even further than
- // usual here by the presence of wxScrollHelperEvtHandler in the event handlers
- // chain and the fact that OnDraw() virtual method must be called if EVT_PAINT
- // is not handled
- class TestScrollWindow : public wxScrolledWindow
- {
- public:
- TestScrollWindow(wxWindow *parent)
- : wxScrolledWindow(parent, wxID_ANY)
- {
- Connect(wxEVT_PAINT, wxPaintEventHandler(TestScrollWindow::OnPaint));
- }
- void GeneratePaintEvent()
- {
- #ifdef __WXGTK__
- // We need to map the window, otherwise we're not going to get any
- // paint events for it.
- wxYield();
- // Ignore events generated during the initial mapping.
- g_str.clear();
- #endif // __WXGTK__
- Refresh();
- Update();
- }
- virtual void OnDraw(wxDC& WXUNUSED(dc))
- {
- g_str += 'D'; // draw
- }
- private:
- void OnPaint(wxPaintEvent& event)
- {
- g_str += 'P'; // paint
- event.Skip();
- }
- wxDECLARE_NO_COPY_CLASS(TestScrollWindow);
- };
- int DoFilterEvent(wxEvent& event)
- {
- if ( event.GetEventType() == TEST_EVT ||
- event.GetEventType() == wxEVT_MENU )
- g_str += 'a';
- return -1;
- }
- bool DoProcessEvent(wxEvent& event)
- {
- if ( event.GetEventType() == TEST_EVT ||
- event.GetEventType() == wxEVT_MENU )
- g_str += 'A';
- return false;
- }
- } // anonymous namespace
- // --------------------------------------------------------------------------
- // test class
- // --------------------------------------------------------------------------
- class EventPropagationTestCase : public CppUnit::TestCase
- {
- public:
- EventPropagationTestCase() {}
- virtual void setUp();
- virtual void tearDown();
- private:
- CPPUNIT_TEST_SUITE( EventPropagationTestCase );
- CPPUNIT_TEST( OneHandler );
- CPPUNIT_TEST( TwoHandlers );
- CPPUNIT_TEST( WindowWithoutHandler );
- CPPUNIT_TEST( WindowWithHandler );
- CPPUNIT_TEST( ForwardEvent );
- CPPUNIT_TEST( ScrollWindowWithoutHandler );
- CPPUNIT_TEST( ScrollWindowWithHandler );
- CPPUNIT_TEST( MenuEvent );
- CPPUNIT_TEST( DocView );
- WXUISIM_TEST( ContextMenuEvent );
- CPPUNIT_TEST_SUITE_END();
- void OneHandler();
- void TwoHandlers();
- void WindowWithoutHandler();
- void WindowWithHandler();
- void ForwardEvent();
- void ScrollWindowWithoutHandler();
- void ScrollWindowWithHandler();
- void MenuEvent();
- void DocView();
- void ContextMenuEvent();
- DECLARE_NO_COPY_CLASS(EventPropagationTestCase)
- };
- // register in the unnamed registry so that these tests are run by default
- CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase );
- // also include in its own registry so that these tests can be run alone
- CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EventPropagationTestCase, "EventPropagationTestCase" );
- void EventPropagationTestCase::setUp()
- {
- SetFilterEventFunc(DoFilterEvent);
- SetProcessEventFunc(DoProcessEvent);
- g_str.clear();
- }
- void EventPropagationTestCase::tearDown()
- {
- SetFilterEventFunc(NULL);
- SetProcessEventFunc(NULL);
- }
- void EventPropagationTestCase::OneHandler()
- {
- wxCommandEvent event(TEST_EVT);
- TestEvtHandler h1('1');
- h1.ProcessEvent(event);
- CPPUNIT_ASSERT_EQUAL( "oa1A", g_str );
- }
- void EventPropagationTestCase::TwoHandlers()
- {
- wxCommandEvent event(TEST_EVT);
- TestEvtHandler h1('1');
- TestEvtHandler h2('2');
- h1.SetNextHandler(&h2);
- h2.SetPreviousHandler(&h1);
- h1.ProcessEvent(event);
- CPPUNIT_ASSERT_EQUAL( "oa1o2A", g_str );
- }
- void EventPropagationTestCase::WindowWithoutHandler()
- {
- wxCommandEvent event(TEST_EVT);
- TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
- wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
- TestWindow * const child = new TestWindow(parent, 'c');
- child->GetEventHandler()->ProcessEvent(event);
- CPPUNIT_ASSERT_EQUAL( "acpA", g_str );
- }
- void EventPropagationTestCase::WindowWithHandler()
- {
- wxCommandEvent event(TEST_EVT);
- TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
- wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
- TestWindow * const child = new TestWindow(parent, 'c');
- TestEvtHandler h1('1');
- child->PushEventHandler(&h1);
- wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
- TestEvtHandler h2('2');
- child->PushEventHandler(&h2);
- wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
- child->HandleWindowEvent(event);
- CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str );
- }
- void EventPropagationTestCase::ForwardEvent()
- {
- // The idea of this test is to check that the events explicitly forwarded
- // to another event handler still get pre/post-processed as usual as this
- // used to be broken by the fixes trying to avoid duplicate processing.
- TestWindow * const win = new TestWindow(wxTheApp->GetTopWindow(), 'w');
- wxON_BLOCK_EXIT_OBJ0( *win, wxWindow::Destroy );
- TestEvtHandler h1('1');
- win->PushEventHandler(&h1);
- wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
- class ForwardEvtHandler : public wxEvtHandler
- {
- public:
- ForwardEvtHandler(wxEvtHandler& h) : m_h(&h) { }
- virtual bool ProcessEvent(wxEvent& event)
- {
- g_str += 'f';
- return m_h->ProcessEvent(event);
- }
- private:
- wxEvtHandler *m_h;
- } f(h1);
- // First send the event directly to f.
- wxCommandEvent event1(TEST_EVT);
- f.ProcessEvent(event1);
- CPPUNIT_ASSERT_EQUAL( "foa1wA", g_str );
- g_str.clear();
- // And then also test sending it to f indirectly.
- wxCommandEvent event2(TEST_EVT);
- TestEvtHandler h2('2');
- h2.SetNextHandler(&f);
- h2.ProcessEvent(event2);
- CPPUNIT_ASSERT_EQUAL( "oa2fo1wAA", g_str );
- }
- void EventPropagationTestCase::ScrollWindowWithoutHandler()
- {
- TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
- wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
- TestScrollWindow * const win = new TestScrollWindow(parent);
- #ifdef CAN_TEST_PAINT_EVENTS
- win->GeneratePaintEvent();
- CPPUNIT_ASSERT_EQUAL( "PD", g_str );
- #endif
- g_str.clear();
- wxCommandEvent eventCmd(TEST_EVT);
- win->HandleWindowEvent(eventCmd);
- CPPUNIT_ASSERT_EQUAL( "apA", g_str );
- }
- void EventPropagationTestCase::ScrollWindowWithHandler()
- {
- TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
- wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
- TestScrollWindow * const win = new TestScrollWindow(parent);
- #ifdef CAN_TEST_PAINT_EVENTS
- TestPaintEvtHandler h('h');
- win->PushEventHandler(&h);
- wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
- win->GeneratePaintEvent();
- CPPUNIT_ASSERT_EQUAL( "ohPD", g_str );
- #endif
- g_str.clear();
- wxCommandEvent eventCmd(TEST_EVT);
- win->HandleWindowEvent(eventCmd);
- CPPUNIT_ASSERT_EQUAL( "apA", g_str );
- }
- // Create a menu bar with a single menu containing wxID_APPLY menu item and
- // attach it to the specified frame.
- wxMenu* CreateTestMenu(wxFrame* frame)
- {
- wxMenu* const menu = new wxMenu;
- menu->Append(wxID_APPLY);
- wxMenuBar* const mb = new wxMenuBar;
- mb->Append(menu, "&Menu");
- frame->SetMenuBar(mb);
- return menu;
- }
- // Helper for checking that the menu event processing resulted in the expected
- // output from the handlers.
- //
- // Notice that this is supposed to be used with ASSERT_MENU_EVENT_RESULT()
- // macro to make the file name and line number of the caller appear in the
- // failure messages.
- void
- CheckMenuEvent(wxMenu* menu, const char* result, CppUnit::SourceLine sourceLine)
- {
- g_str.clear();
- // Trigger the menu event: this is more reliable than using
- // wxUIActionSimulator and currently works in all ports as they all call
- // wxMenuBase::SendEvent() from their respective menu event handlers.
- menu->SendEvent(wxID_APPLY);
- CPPUNIT_NS::assertEquals( result, g_str, sourceLine, "" );
- }
- #define ASSERT_MENU_EVENT_RESULT(menu, result) \
- CheckMenuEvent((menu), (result), CPPUNIT_SOURCELINE())
- void EventPropagationTestCase::MenuEvent()
- {
- wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow());
- // Create a minimal menu bar.
- wxMenu* const menu = CreateTestMenu(frame);
- wxMenuBar* const mb = menu->GetMenuBar();
- wxScopedPtr<wxMenuBar> ensureMenuBarDestruction(mb);
- wxON_BLOCK_EXIT_OBJ1( *frame, wxFrame::SetMenuBar, (wxMenuBar*)NULL );
- // Check that wxApp gets the event exactly once.
- ASSERT_MENU_EVENT_RESULT( menu, "aA" );
- // Check that the menu event handler is called.
- TestMenuEvtHandler hm('m'); // 'm' for "menu"
- menu->SetNextHandler(&hm);
- wxON_BLOCK_EXIT_OBJ1( *menu,
- wxEvtHandler::SetNextHandler, (wxEvtHandler*)NULL );
- ASSERT_MENU_EVENT_RESULT( menu, "aomA" );
- // Test that the event handler associated with the menu bar gets the event.
- TestMenuEvtHandler hb('b'); // 'b' for "menu Bar"
- mb->PushEventHandler(&hb);
- wxON_BLOCK_EXIT_OBJ1( *mb, wxWindow::PopEventHandler, false );
- ASSERT_MENU_EVENT_RESULT( menu, "aomobA" );
- // Also test that the window to which the menu belongs gets the event.
- TestMenuEvtHandler hw('w'); // 'w' for "Window"
- frame->PushEventHandler(&hw);
- wxON_BLOCK_EXIT_OBJ1( *frame, wxWindow::PopEventHandler, false );
- ASSERT_MENU_EVENT_RESULT( menu, "aomobowA" );
- }
- // Minimal viable implementations of wxDocument and wxView.
- class EventTestDocument : public wxDocument
- {
- public:
- EventTestDocument() { }
- wxDECLARE_DYNAMIC_CLASS(EventTestDocument);
- };
- class EventTestView : public wxView
- {
- public:
- EventTestView() { }
- virtual void OnDraw(wxDC*) { }
- wxDECLARE_DYNAMIC_CLASS(EventTestView);
- };
- wxIMPLEMENT_DYNAMIC_CLASS(EventTestDocument, wxDocument);
- wxIMPLEMENT_DYNAMIC_CLASS(EventTestView, wxView);
- void EventPropagationTestCase::DocView()
- {
- // Set up the parent frame and its menu bar.
- wxDocManager docManager;
- wxScopedPtr<wxDocMDIParentFrame>
- parent(new wxDocMDIParentFrame(&docManager, NULL, wxID_ANY, "Parent"));
- wxMenu* const menu = CreateTestMenu(parent.get());
- // Set up the event handlers.
- TestEvtSink sinkDM('m');
- docManager.Connect(wxEVT_MENU,
- wxEventHandler(TestEvtSink::Handle), NULL, &sinkDM);
- TestEvtSink sinkParent('p');
- parent->Connect(wxEVT_MENU,
- wxEventHandler(TestEvtSink::Handle), NULL, &sinkParent);
- // Check that wxDocManager and wxFrame get the event in order.
- ASSERT_MENU_EVENT_RESULT( menu, "ampA" );
- // Now check what happens if we have an active document.
- wxDocTemplate docTemplate(&docManager, "Test", "", "", "",
- "Test Document", "Test View",
- wxCLASSINFO(EventTestDocument),
- wxCLASSINFO(EventTestView));
- wxDocument* const doc = docTemplate.CreateDocument("");
- wxView* const view = doc->GetFirstView();
- wxScopedPtr<wxMDIChildFrame>
- child(new wxDocMDIChildFrame(doc, view, parent.get(), wxID_ANY, "Child"));
- wxMenu* const menuChild = CreateTestMenu(child.get());
- // Ensure that the child that we've just created is the active one.
- child->Activate();
- #ifdef __WXGTK__
- // There are a lot of hacks related to child frame menu bar handling in
- // wxGTK and, in particular, the code in src/gtk/mdi.cpp relies on getting
- // idle events to really put everything in place. Moreover, as wxGTK uses
- // GtkNotebook as its MDI pages container, the frame must be shown for all
- // this to work as gtk_notebook_set_current_page() doesn't do anything if
- // called for a hidden window (this incredible fact cost me quite some time
- // to find empirically -- only to notice its confirmation in GTK+
- // documentation immediately afterwards). So just do whatever it takes to
- // make things work "as usual".
- child->Show();
- parent->Show();
- wxYield();
- #endif // __WXGTK__
- TestEvtSink sinkDoc('d');
- doc->Connect(wxEVT_MENU,
- wxEventHandler(TestEvtSink::Handle), NULL, &sinkDoc);
- TestEvtSink sinkView('v');
- view->Connect(wxEVT_MENU,
- wxEventHandler(TestEvtSink::Handle), NULL, &sinkView);
- TestEvtSink sinkChild('c');
- child->Connect(wxEVT_MENU,
- wxEventHandler(TestEvtSink::Handle), NULL, &sinkChild);
- // Check that wxDocument, wxView, wxDocManager, child frame and the parent
- // get the event in order.
- ASSERT_MENU_EVENT_RESULT( menuChild, "advmcpA" );
- #if wxUSE_TOOLBAR
- // Also check that toolbar events get forwarded to the active child.
- wxToolBar* const tb = parent->CreateToolBar(wxTB_NOICONS);
- tb->AddTool(wxID_APPLY, "Apply", wxNullBitmap);
- tb->Realize();
- // As in CheckMenuEvent(), use toolbar method actually sending the event
- // instead of bothering with wxUIActionSimulator which would have been
- // trickier.
- g_str.clear();
- tb->OnLeftClick(wxID_APPLY, true /* doesn't matter */);
- CPPUNIT_ASSERT_EQUAL( "advmcpA", g_str );
- #endif // wxUSE_TOOLBAR
- }
- #if wxUSE_UIACTIONSIMULATOR
- class ContextMenuTestWindow : public wxWindow
- {
- public:
- ContextMenuTestWindow(wxWindow *parent, char tag)
- : wxWindow(parent, wxID_ANY),
- m_tag(tag)
- {
- Connect(wxEVT_CONTEXT_MENU,
- wxContextMenuEventHandler(ContextMenuTestWindow::OnMenu));
- }
- private:
- void OnMenu(wxContextMenuEvent& event)
- {
- g_str += m_tag;
- event.Skip();
- }
- const char m_tag;
- wxDECLARE_NO_COPY_CLASS(ContextMenuTestWindow);
- };
- void EventPropagationTestCase::ContextMenuEvent()
- {
- ContextMenuTestWindow * const
- parent = new ContextMenuTestWindow(wxTheApp->GetTopWindow(), 'p');
- wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
- ContextMenuTestWindow * const
- child = new ContextMenuTestWindow(parent, 'c');
- parent->SetSize(100, 100);
- child->SetSize(0, 0, 50, 50);
- child->SetFocus();
- wxUIActionSimulator sim;
- const wxPoint origin = parent->ClientToScreen(wxPoint(0, 0));
- // Right clicking in the child should generate an event for it and the
- // parent.
- g_str.clear();
- sim.MouseMove(origin + wxPoint(10, 10));
- sim.MouseClick(wxMOUSE_BTN_RIGHT);
- // At least with MSW, for WM_CONTEXTMENU to be synthesized by the system
- // from the right mouse click event, we must dispatch the mouse messages.
- wxYield();
- CPPUNIT_ASSERT_EQUAL( "cp", g_str );
- // Right clicking outside the child should generate the event just in the
- // parent.
- g_str.clear();
- sim.MouseMove(origin + wxPoint(60, 60));
- sim.MouseClick(wxMOUSE_BTN_RIGHT);
- wxYield();
- CPPUNIT_ASSERT_EQUAL( "p", g_str );
- }
- #endif // wxUSE_UIACTIONSIMULATOR
|