]> git.saurik.com Git - wxWidgets.git/commitdiff
Better fix for duplicate wxContextMenuEvent generation under MSW.
authorVadim Zeitlin <vadim@wxwidgets.org>
Tue, 2 Jul 2013 20:24:22 +0000 (20:24 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Tue, 2 Jul 2013 20:24:22 +0000 (20:24 +0000)
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

src/msw/window.cpp
tests/events/propagation.cpp

index 08710f5f7b762eb7bb1d151a5d089f06665ee7ac..60fa618ccedcd49600809a9170d357b8fd02b792 100644 (file)
@@ -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
index 5cd90ae072ea74481e95a5f41f6d95be1d385122..53441b75c7be8354ea028cd6005817c72ec76693 100644 (file)
@@ -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