2009-01-16 11:21:50 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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
|
2009-01-17 06:38:32 -05:00
|
|
|
#include "wx/app.h"
|
|
|
|
#include "wx/event.h"
|
2010-05-20 13:33:26 -04:00
|
|
|
#include "wx/scrolwin.h"
|
2009-01-17 06:38:32 -05:00
|
|
|
#include "wx/window.h"
|
2009-01-16 11:21:50 -05:00
|
|
|
#endif // WX_PRECOMP
|
|
|
|
|
2013-05-04 19:59:56 -04:00
|
|
|
#include "wx/docmdi.h"
|
2013-05-04 19:59:29 -04:00
|
|
|
#include "wx/frame.h"
|
|
|
|
#include "wx/menu.h"
|
2013-05-04 19:59:48 -04:00
|
|
|
#include "wx/scopedptr.h"
|
2009-01-16 11:21:50 -05:00
|
|
|
#include "wx/scopeguard.h"
|
2013-07-03 18:18:18 -04:00
|
|
|
#include "wx/toolbar.h"
|
2013-07-02 16:24:22 -04:00
|
|
|
#include "wx/uiaction.h"
|
2009-01-16 11:21:50 -05:00
|
|
|
|
2013-05-04 20:00:01 -04:00
|
|
|
// 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.
|
2017-11-07 10:37:25 -05:00
|
|
|
//
|
|
|
|
// FIXME: Under GTK+ 3 the test is broken because a simple wxYield() is not
|
|
|
|
// enough to map the frame. It should be also fixed there by waiting for
|
|
|
|
// it to come up, with some timeout, but for now it always fails, so
|
|
|
|
// it's useless to run it.
|
|
|
|
#if !defined(__WXOSX__) && !defined(__WXGTK3__)
|
2013-05-04 20:00:01 -04:00
|
|
|
#define CAN_TEST_PAINT_EVENTS
|
|
|
|
#endif
|
|
|
|
|
2009-01-16 11:21:50 -05:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
// this string will record the execution of all handlers
|
|
|
|
wxString g_str;
|
|
|
|
|
|
|
|
// a custom event
|
|
|
|
wxDEFINE_EVENT(TEST_EVT, wxCommandEvent);
|
|
|
|
|
2010-05-20 13:33:26 -04:00
|
|
|
// a custom event handler tracing the propagation of the events of the
|
|
|
|
// specified types
|
|
|
|
template <class Event>
|
|
|
|
class TestEvtHandlerBase : public wxEvtHandler
|
2009-01-16 11:21:50 -05:00
|
|
|
{
|
|
|
|
public:
|
2010-05-20 13:33:26 -04:00
|
|
|
TestEvtHandlerBase(wxEventType evtType, char tag)
|
|
|
|
: m_evtType(evtType),
|
|
|
|
m_tag(tag)
|
2009-01-16 11:21:50 -05:00
|
|
|
{
|
2010-05-20 13:33:26 -04:00
|
|
|
Connect(evtType,
|
|
|
|
static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest));
|
2009-01-16 11:21:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// override ProcessEvent() to confirm that it is called for all event
|
|
|
|
// handlers in the chain
|
2018-09-21 13:46:49 -04:00
|
|
|
virtual bool ProcessEvent(wxEvent& event) wxOVERRIDE
|
2009-01-16 11:21:50 -05:00
|
|
|
{
|
2010-05-20 13:33:26 -04:00
|
|
|
if ( event.GetEventType() == m_evtType )
|
2009-01-16 11:21:50 -05:00
|
|
|
g_str += 'o'; // "o" == "overridden"
|
|
|
|
|
|
|
|
return wxEvtHandler::ProcessEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2010-05-20 13:33:26 -04:00
|
|
|
void OnTest(wxEvent& event)
|
2009-01-16 11:21:50 -05:00
|
|
|
{
|
|
|
|
g_str += m_tag;
|
|
|
|
|
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
|
2010-05-20 13:33:26 -04:00
|
|
|
const wxEventType m_evtType;
|
2009-01-16 11:21:50 -05:00
|
|
|
const char m_tag;
|
|
|
|
|
2010-05-20 13:33:26 -04:00
|
|
|
wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase, Event);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct TestEvtHandler : TestEvtHandlerBase<wxCommandEvent>
|
|
|
|
{
|
|
|
|
TestEvtHandler(char tag)
|
|
|
|
: TestEvtHandlerBase<wxCommandEvent>(TEST_EVT, tag)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-05-04 19:59:29 -04:00
|
|
|
struct TestMenuEvtHandler : TestEvtHandlerBase<wxCommandEvent>
|
|
|
|
{
|
|
|
|
TestMenuEvtHandler(char tag)
|
|
|
|
: TestEvtHandlerBase<wxCommandEvent>(wxEVT_MENU, tag)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-05-20 13:33:26 -04:00
|
|
|
struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
|
|
|
|
{
|
|
|
|
TestPaintEvtHandler(char tag)
|
|
|
|
: TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag)
|
|
|
|
{
|
|
|
|
}
|
2009-01-16 11:21:50 -05:00
|
|
|
};
|
|
|
|
|
2013-05-04 19:59:56 -04:00
|
|
|
// 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);
|
|
|
|
};
|
|
|
|
|
2009-01-16 11:21:50 -05:00
|
|
|
// 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;
|
|
|
|
|
2015-04-23 07:49:01 -04:00
|
|
|
wxDECLARE_NO_COPY_CLASS(TestWindow);
|
2009-01-16 11:21:50 -05:00
|
|
|
};
|
|
|
|
|
2010-05-20 13:33:26 -04:00
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
|
2013-05-04 20:00:01 -04:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-09-21 13:46:49 -04:00
|
|
|
virtual void OnDraw(wxDC& WXUNUSED(dc)) wxOVERRIDE
|
2010-05-20 13:33:26 -04:00
|
|
|
{
|
|
|
|
g_str += 'D'; // draw
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void OnPaint(wxPaintEvent& event)
|
|
|
|
{
|
|
|
|
g_str += 'P'; // paint
|
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
wxDECLARE_NO_COPY_CLASS(TestScrollWindow);
|
|
|
|
};
|
|
|
|
|
2009-01-16 11:21:50 -05:00
|
|
|
int DoFilterEvent(wxEvent& event)
|
|
|
|
{
|
2013-05-04 19:59:29 -04:00
|
|
|
if ( event.GetEventType() == TEST_EVT ||
|
|
|
|
event.GetEventType() == wxEVT_MENU )
|
2009-01-16 11:21:50 -05:00
|
|
|
g_str += 'a';
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DoProcessEvent(wxEvent& event)
|
|
|
|
{
|
2013-05-04 19:59:29 -04:00
|
|
|
if ( event.GetEventType() == TEST_EVT ||
|
|
|
|
event.GetEventType() == wxEVT_MENU )
|
2009-01-16 11:21:50 -05:00
|
|
|
g_str += 'A';
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// test class
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class EventPropagationTestCase : public CppUnit::TestCase
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
EventPropagationTestCase() {}
|
|
|
|
|
2018-07-29 05:09:17 -04:00
|
|
|
virtual void setUp() wxOVERRIDE;
|
|
|
|
virtual void tearDown() wxOVERRIDE;
|
2009-01-16 11:21:50 -05:00
|
|
|
|
|
|
|
private:
|
|
|
|
CPPUNIT_TEST_SUITE( EventPropagationTestCase );
|
|
|
|
CPPUNIT_TEST( OneHandler );
|
|
|
|
CPPUNIT_TEST( TwoHandlers );
|
|
|
|
CPPUNIT_TEST( WindowWithoutHandler );
|
|
|
|
CPPUNIT_TEST( WindowWithHandler );
|
2010-06-02 07:58:31 -04:00
|
|
|
CPPUNIT_TEST( ForwardEvent );
|
2010-05-20 13:33:26 -04:00
|
|
|
CPPUNIT_TEST( ScrollWindowWithoutHandler );
|
|
|
|
CPPUNIT_TEST( ScrollWindowWithHandler );
|
2014-09-23 13:44:45 -04:00
|
|
|
// for unknown reason, this test will cause the tests segmentation failed
|
|
|
|
// under x11, disable it for now.
|
|
|
|
#if !defined (__WXX11__)
|
|
|
|
CPPUNIT_TEST( MenuEvent );
|
|
|
|
#endif
|
2013-05-04 19:59:56 -04:00
|
|
|
CPPUNIT_TEST( DocView );
|
2013-07-02 16:24:22 -04:00
|
|
|
WXUISIM_TEST( ContextMenuEvent );
|
2017-10-01 12:44:53 -04:00
|
|
|
WXUISIM_TEST( PropagationLevel );
|
2009-01-16 11:21:50 -05:00
|
|
|
CPPUNIT_TEST_SUITE_END();
|
|
|
|
|
|
|
|
void OneHandler();
|
|
|
|
void TwoHandlers();
|
|
|
|
void WindowWithoutHandler();
|
|
|
|
void WindowWithHandler();
|
2010-06-02 07:58:31 -04:00
|
|
|
void ForwardEvent();
|
2010-05-20 13:33:26 -04:00
|
|
|
void ScrollWindowWithoutHandler();
|
|
|
|
void ScrollWindowWithHandler();
|
2013-05-04 19:59:29 -04:00
|
|
|
void MenuEvent();
|
2013-05-04 19:59:56 -04:00
|
|
|
void DocView();
|
2017-10-01 12:44:53 -04:00
|
|
|
#if wxUSE_UIACTIONSIMULATOR
|
2013-07-02 16:24:22 -04:00
|
|
|
void ContextMenuEvent();
|
2016-01-29 23:00:43 -05:00
|
|
|
void PropagationLevel();
|
2017-10-01 12:44:53 -04:00
|
|
|
#endif
|
2009-01-16 11:21:50 -05:00
|
|
|
|
2015-04-23 07:49:01 -04:00
|
|
|
wxDECLARE_NO_COPY_CLASS(EventPropagationTestCase);
|
2009-01-16 11:21:50 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
// register in the unnamed registry so that these tests are run by default
|
|
|
|
CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase );
|
|
|
|
|
2011-04-30 06:57:04 -04:00
|
|
|
// also include in its own registry so that these tests can be run alone
|
2009-01-16 11:21:50 -05:00
|
|
|
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');
|
|
|
|
|
2009-01-25 06:58:39 -05:00
|
|
|
child->GetEventHandler()->ProcessEvent(event);
|
2009-01-16 11:21:50 -05:00
|
|
|
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);
|
2009-01-17 08:01:47 -05:00
|
|
|
wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
|
2009-01-16 11:21:50 -05:00
|
|
|
TestEvtHandler h2('2');
|
|
|
|
child->PushEventHandler(&h2);
|
2009-01-17 08:01:47 -05:00
|
|
|
wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
|
2009-01-16 11:21:50 -05:00
|
|
|
|
|
|
|
child->HandleWindowEvent(event);
|
|
|
|
CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str );
|
|
|
|
}
|
|
|
|
|
2010-06-02 07:58:31 -04:00
|
|
|
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) { }
|
|
|
|
|
2018-09-21 13:46:49 -04:00
|
|
|
virtual bool ProcessEvent(wxEvent& event) wxOVERRIDE
|
2010-06-02 07:58:31 -04:00
|
|
|
{
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2010-05-20 13:33:26 -04:00
|
|
|
void EventPropagationTestCase::ScrollWindowWithoutHandler()
|
|
|
|
{
|
2010-05-21 08:07:45 -04:00
|
|
|
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
|
|
|
|
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
|
|
|
|
|
|
|
|
TestScrollWindow * const win = new TestScrollWindow(parent);
|
2010-05-20 13:33:26 -04:00
|
|
|
|
2013-05-04 20:00:01 -04:00
|
|
|
#ifdef CAN_TEST_PAINT_EVENTS
|
|
|
|
win->GeneratePaintEvent();
|
2010-05-20 13:33:26 -04:00
|
|
|
CPPUNIT_ASSERT_EQUAL( "PD", g_str );
|
2010-06-22 08:46:20 -04:00
|
|
|
#endif
|
2013-05-04 20:00:01 -04:00
|
|
|
|
2010-05-21 08:07:45 -04:00
|
|
|
g_str.clear();
|
|
|
|
wxCommandEvent eventCmd(TEST_EVT);
|
|
|
|
win->HandleWindowEvent(eventCmd);
|
|
|
|
CPPUNIT_ASSERT_EQUAL( "apA", g_str );
|
2010-05-20 13:33:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void EventPropagationTestCase::ScrollWindowWithHandler()
|
|
|
|
{
|
2010-05-21 08:07:45 -04:00
|
|
|
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
|
|
|
|
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
|
|
|
|
|
|
|
|
TestScrollWindow * const win = new TestScrollWindow(parent);
|
2010-05-20 13:33:26 -04:00
|
|
|
|
2013-05-04 20:00:01 -04:00
|
|
|
#ifdef CAN_TEST_PAINT_EVENTS
|
2010-05-20 13:33:26 -04:00
|
|
|
TestPaintEvtHandler h('h');
|
|
|
|
win->PushEventHandler(&h);
|
|
|
|
wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
|
|
|
|
|
2013-05-04 20:00:01 -04:00
|
|
|
win->GeneratePaintEvent();
|
2010-05-20 13:33:26 -04:00
|
|
|
CPPUNIT_ASSERT_EQUAL( "ohPD", g_str );
|
2010-06-22 09:48:17 -04:00
|
|
|
#endif
|
2010-05-21 08:07:45 -04:00
|
|
|
|
|
|
|
g_str.clear();
|
|
|
|
wxCommandEvent eventCmd(TEST_EVT);
|
|
|
|
win->HandleWindowEvent(eventCmd);
|
|
|
|
CPPUNIT_ASSERT_EQUAL( "apA", g_str );
|
2010-05-20 13:33:26 -04:00
|
|
|
}
|
|
|
|
|
2013-05-04 19:59:48 -04:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2013-05-04 19:59:29 -04:00
|
|
|
// Helper for checking that the menu event processing resulted in the expected
|
|
|
|
// output from the handlers.
|
2013-05-04 19:59:40 -04:00
|
|
|
//
|
Replace CppUnit with Catch for unit tests
Drop the legacy CppUnit testing framework used for the unit tests.
Replacing it with Catch has the advantage of not requiring CppUnit
libraries to be installed on the system in order to be able to run
tests (Catch is header-only and a copy of it is now included in the
main repository itself) and, in the future, of being able to write
the tests in a much more natural way.
For now, however, avoid changing the existing tests code as much as
[reasonably] possible to avoid introducing bugs in them and provide
the CppUnit compatibility macros in the new wx/catch_cppunit.h header
which allow to preserve the 99% of the existing code unchanged. Some
of the required changes are:
- Decompose asserts using "a && b" conditions into multiple asserts
checking "a" and "b" independently. This would have been better
even with CppUnit (to know which part of condition exactly failed)
and is required with Catch.
- Use extra parentheses around such conditions when they can't be
easily decomposed in the arrays test, due to the use of macros.
This is not ideal from the point of view of messages given when
the tests fail but will do for now.
- Rewrite asserts using "a || b" as a combination of condition
checks and assert macros. Again, this is better anyhow, and is
required with Catch. Incidentally, this allowed to fix a bug in
the "exec" unit test which didn't leave enough time for the new
process to be launched before trying to kill it.
- Remove multiple CPPUNIT_TEST_SUITE_NAMED_REGISTRATION() macros,
our emulation of this macro can be used only once.
- Provide string conversions using Catch-specific StringMaker for
a couple of types.
- Replace custom wxImage comparison with a Catch-specific matcher
class.
- Remove most of test running logic from test.cpp, in particular don't
parse command line ourselves any longer but use Catch built-in
command line parser. This is a source of a minor regression:
previously, both "Foo" and "FooTestCase" could be used as the name of
the test to run, but now only the latter is accepted.
2017-11-01 14:15:24 -04:00
|
|
|
// Note that we trigger the menu event by sending it directly as 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.
|
2018-08-24 13:03:15 -04:00
|
|
|
#define ASSERT_MENU_EVENT_RESULT_FOR(cmd, menu, result) \
|
|
|
|
g_str.clear(); \
|
|
|
|
menu->SendEvent(cmd); \
|
Replace CppUnit with Catch for unit tests
Drop the legacy CppUnit testing framework used for the unit tests.
Replacing it with Catch has the advantage of not requiring CppUnit
libraries to be installed on the system in order to be able to run
tests (Catch is header-only and a copy of it is now included in the
main repository itself) and, in the future, of being able to write
the tests in a much more natural way.
For now, however, avoid changing the existing tests code as much as
[reasonably] possible to avoid introducing bugs in them and provide
the CppUnit compatibility macros in the new wx/catch_cppunit.h header
which allow to preserve the 99% of the existing code unchanged. Some
of the required changes are:
- Decompose asserts using "a && b" conditions into multiple asserts
checking "a" and "b" independently. This would have been better
even with CppUnit (to know which part of condition exactly failed)
and is required with Catch.
- Use extra parentheses around such conditions when they can't be
easily decomposed in the arrays test, due to the use of macros.
This is not ideal from the point of view of messages given when
the tests fail but will do for now.
- Rewrite asserts using "a || b" as a combination of condition
checks and assert macros. Again, this is better anyhow, and is
required with Catch. Incidentally, this allowed to fix a bug in
the "exec" unit test which didn't leave enough time for the new
process to be launched before trying to kill it.
- Remove multiple CPPUNIT_TEST_SUITE_NAMED_REGISTRATION() macros,
our emulation of this macro can be used only once.
- Provide string conversions using Catch-specific StringMaker for
a couple of types.
- Replace custom wxImage comparison with a Catch-specific matcher
class.
- Remove most of test running logic from test.cpp, in particular don't
parse command line ourselves any longer but use Catch built-in
command line parser. This is a source of a minor regression:
previously, both "Foo" and "FooTestCase" could be used as the name of
the test to run, but now only the latter is accepted.
2017-11-01 14:15:24 -04:00
|
|
|
CHECK( g_str == result )
|
2013-05-04 19:59:40 -04:00
|
|
|
|
2018-08-24 13:03:15 -04:00
|
|
|
#define ASSERT_MENU_EVENT_RESULT(menu, result) \
|
|
|
|
ASSERT_MENU_EVENT_RESULT_FOR(wxID_APPLY, menu, result)
|
|
|
|
|
2013-05-04 19:59:29 -04:00
|
|
|
void EventPropagationTestCase::MenuEvent()
|
|
|
|
{
|
|
|
|
wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow());
|
2013-05-04 19:59:48 -04:00
|
|
|
|
|
|
|
// Create a minimal menu bar.
|
|
|
|
wxMenu* const menu = CreateTestMenu(frame);
|
|
|
|
wxMenuBar* const mb = menu->GetMenuBar();
|
|
|
|
wxScopedPtr<wxMenuBar> ensureMenuBarDestruction(mb);
|
2013-05-04 19:59:29 -04:00
|
|
|
wxON_BLOCK_EXIT_OBJ1( *frame, wxFrame::SetMenuBar, (wxMenuBar*)NULL );
|
|
|
|
|
|
|
|
// Check that wxApp gets the event exactly once.
|
2013-05-04 19:59:40 -04:00
|
|
|
ASSERT_MENU_EVENT_RESULT( menu, "aA" );
|
2013-05-04 19:59:29 -04:00
|
|
|
|
|
|
|
|
|
|
|
// 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 );
|
2013-05-04 19:59:40 -04:00
|
|
|
ASSERT_MENU_EVENT_RESULT( menu, "aomA" );
|
2013-05-04 19:59:29 -04:00
|
|
|
|
|
|
|
|
2018-08-24 13:03:15 -04:00
|
|
|
// Check that a handler can also be attached to a submenu.
|
|
|
|
wxMenu* const submenu = new wxMenu;
|
|
|
|
submenu->Append(wxID_ABOUT);
|
|
|
|
menu->Append(wxID_ANY, "Submenu", submenu);
|
|
|
|
|
|
|
|
TestMenuEvtHandler hs('s'); // 's' for "submenu"
|
|
|
|
submenu->SetNextHandler(&hs);
|
|
|
|
wxON_BLOCK_EXIT_OBJ1( *submenu,
|
|
|
|
wxEvtHandler::SetNextHandler, (wxEvtHandler*)NULL );
|
|
|
|
ASSERT_MENU_EVENT_RESULT_FOR( wxID_ABOUT, submenu, "aosomA" );
|
|
|
|
|
2013-05-04 19:59:32 -04:00
|
|
|
// 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 );
|
|
|
|
|
2013-05-04 19:59:40 -04:00
|
|
|
ASSERT_MENU_EVENT_RESULT( menu, "aomobA" );
|
2013-05-04 19:59:32 -04:00
|
|
|
|
|
|
|
|
2013-05-04 19:59:29 -04:00
|
|
|
// 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 );
|
|
|
|
|
2013-05-04 19:59:40 -04:00
|
|
|
ASSERT_MENU_EVENT_RESULT( menu, "aomobowA" );
|
2013-05-04 19:59:29 -04:00
|
|
|
}
|
2013-05-04 19:59:56 -04:00
|
|
|
|
|
|
|
// Minimal viable implementations of wxDocument and wxView.
|
|
|
|
class EventTestDocument : public wxDocument
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
EventTestDocument() { }
|
|
|
|
|
|
|
|
wxDECLARE_DYNAMIC_CLASS(EventTestDocument);
|
|
|
|
};
|
|
|
|
|
|
|
|
class EventTestView : public wxView
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
EventTestView() { }
|
|
|
|
|
2018-09-21 13:46:49 -04:00
|
|
|
virtual void OnDraw(wxDC*) wxOVERRIDE { }
|
2013-05-04 19:59:56 -04:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2013-07-03 18:17:55 -04:00
|
|
|
wxScopedPtr<wxMDIChildFrame>
|
2013-05-04 19:59:56 -04:00
|
|
|
child(new wxDocMDIChildFrame(doc, view, parent.get(), wxID_ANY, "Child"));
|
|
|
|
|
|
|
|
wxMenu* const menuChild = CreateTestMenu(child.get());
|
|
|
|
|
2013-07-03 18:17:55 -04:00
|
|
|
// Ensure that the child that we've just created is the active one.
|
|
|
|
child->Activate();
|
|
|
|
|
2013-05-04 19:59:56 -04:00
|
|
|
#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" );
|
2013-07-03 18:18:18 -04:00
|
|
|
|
|
|
|
|
|
|
|
#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
|
2013-05-04 19:59:56 -04:00
|
|
|
}
|
2013-07-02 16:24:22 -04:00
|
|
|
|
|
|
|
#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 );
|
|
|
|
|
2015-06-12 09:57:33 -04:00
|
|
|
// For some unfathomable reason the test below sporadically fails in wxGTK
|
|
|
|
// buildbot builds, so disable it there to avoid spurious failure reports.
|
|
|
|
#ifdef __WXGTK__
|
|
|
|
if ( IsAutomaticTest() )
|
|
|
|
return;
|
|
|
|
#endif // __WXGTK__
|
|
|
|
|
2013-07-02 16:24:22 -04:00
|
|
|
// 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 );
|
|
|
|
}
|
|
|
|
|
2016-01-29 23:00:43 -05:00
|
|
|
// Helper function: get the event propagation level.
|
|
|
|
int GetPropagationLevel(wxEvent& e)
|
|
|
|
{
|
|
|
|
const int level = e.StopPropagation();
|
|
|
|
e.ResumePropagation(level);
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventPropagationTestCase::PropagationLevel()
|
|
|
|
{
|
|
|
|
wxSizeEvent se;
|
|
|
|
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(se), (int)wxEVENT_PROPAGATE_NONE );
|
|
|
|
|
|
|
|
wxCommandEvent ce;
|
|
|
|
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce), (int)wxEVENT_PROPAGATE_MAX );
|
|
|
|
|
|
|
|
wxCommandEvent ce2(ce);
|
|
|
|
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce2), (int)wxEVENT_PROPAGATE_MAX );
|
|
|
|
|
|
|
|
wxCommandEvent ce3;
|
|
|
|
ce3.ResumePropagation(17);
|
|
|
|
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce3), 17 );
|
|
|
|
|
|
|
|
wxCommandEvent ce4(ce3);
|
|
|
|
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce4), 17 );
|
|
|
|
}
|
|
|
|
|
2013-07-02 16:24:22 -04:00
|
|
|
#endif // wxUSE_UIACTIONSIMULATOR
|