From: Vadim Zeitlin Date: Tue, 2 Jul 2013 20:24:22 +0000 (+0000) Subject: Better fix for duplicate wxContextMenuEvent generation under MSW. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/dbd5b2ce42875058a7f109168d7887c95d893d28 Better fix for duplicate wxContextMenuEvent generation under MSW. Fix the bug with multiple wxContextMenuEvent being generated for a single WM_CONTEXTMENU without breaking context menus for wxTextCtrl (and all the other native controls). Do this by ensuring that WM_CONTEXTMENU is still passed to DefWindowProc() if we don't process it instead of just being eaten completely in any case. Also add a unit test checking for this bug to ensure it stays fixed. See #13683. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74329 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 08710f5f7b..60fa618cce 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3433,32 +3433,31 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, #if !defined(__WXWINCE__) case WM_CONTEXTMENU: { - // As with WM_HELP above, this message is propagated upwards - // the parent chain by DefWindowProc() itself, so we should - // always mark it as processed to prevent it from doing this - // as this would result in duplicate calls to event handlers. - processed = true; + // Ignore the events that are propagated from a child window by + // DefWindowProc(): as wxContextMenuEvent is already propagated + // upwards the window hierarchy by us, not doing this would + // result in duplicate events being sent. + WXHWND hWnd = (WXHWND)wParam; + if ( hWnd != m_hWnd ) + { + wxWindowMSW *win = FindItemByHWND(hWnd); + if ( win && IsDescendant(win) ) + { + // We had already generated wxContextMenuEvent when we + // got WM_CONTEXTMENU for that window. + processed = true; + break; + } + } // we don't convert from screen to client coordinates as // the event may be handled by a parent window wxPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt); + evtCtx.SetEventObject(this); - // we could have got an event from our child, reflect it back - // to it if this is the case - wxWindowMSW *win = NULL; - WXHWND hWnd = (WXHWND)wParam; - if ( hWnd != m_hWnd ) - { - win = FindItemByHWND(hWnd); - } - - if ( !win ) - win = this; - - evtCtx.SetEventObject(win); - win->HandleWindowEvent(evtCtx); + processed = HandleWindowEvent(evtCtx); } break; #endif diff --git a/tests/events/propagation.cpp b/tests/events/propagation.cpp index 5cd90ae072..53441b75c7 100644 --- a/tests/events/propagation.cpp +++ b/tests/events/propagation.cpp @@ -29,6 +29,7 @@ #include "wx/menu.h" #include "wx/scopedptr.h" #include "wx/scopeguard.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 @@ -242,6 +243,7 @@ private: CPPUNIT_TEST( ScrollWindowWithHandler ); CPPUNIT_TEST( MenuEvent ); CPPUNIT_TEST( DocView ); + WXUISIM_TEST( ContextMenuEvent ); CPPUNIT_TEST_SUITE_END(); void OneHandler(); @@ -253,6 +255,7 @@ private: void ScrollWindowWithHandler(); void MenuEvent(); void DocView(); + void ContextMenuEvent(); DECLARE_NO_COPY_CLASS(EventPropagationTestCase) }; @@ -573,3 +576,67 @@ void EventPropagationTestCase::DocView() // get the event in order. ASSERT_MENU_EVENT_RESULT( menuChild, "advmcpA" ); } + +#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