From 5ac5e40e4181aaf7fd79ca445113d7a228a70cb8 Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Wed, 3 Jul 2013 22:18:42 +0000
Subject: [PATCH] Prevent duplicate menu event processing in MDI windows.

Record the object propagating the given event upwards in the event object
itself and use it in wxMDIParentFrame to determine whether the event being
handled is already coming from wxMDIChildFrame and avoid sending it back for
processing it there again in this case.

This is ugly and makes wx event processing even more complex but this is the
only way I could find to ensure that

(a) Both the child and the parent frames get the events from the toolbar
    (even though the toolbar parent is the parent frame and hence normally
    the child wouldn't get notified about them at all and so the forwarding
    at wxMDIParentFrame level is required to make this work).

(b) The child gets the event only once, whether it comes from a toolbar (and
    hence indirectly via the parent frame) or from the child menu (and hence
    directly to the child, at least in wxMSW).

This commit fixes the event propagation unit test case, at least under MSW and
GTK.

See #14314.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74357 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 include/wx/event.h    | 22 +++++++++++++++++++---
 include/wx/mdi.h      | 14 ++++++++++++--
 src/common/event.cpp  |  3 +++
 src/common/wincmn.cpp |  2 +-
 4 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/include/wx/event.h b/include/wx/event.h
index 38303aaae6..874d42e274 100644
--- a/include/wx/event.h
+++ b/include/wx/event.h
@@ -986,6 +986,9 @@ public:
         m_propagationLevel = propagationLevel;
     }
 
+    // This method is for internal use only and allows to get the object that
+    // is propagating this event upwards the window hierarchy, if any.
+    wxEvtHandler* GetPropagatedFrom() const { return m_propagatedFrom; }
 
     // This is for internal use only and is only called by
     // wxEvtHandler::ProcessEvent() to check whether it's the first time this
@@ -1056,6 +1059,10 @@ protected:
     // the parent window (if any)
     int               m_propagationLevel;
 
+    // The object that the event is being propagated from, initially NULL and
+    // only set by wxPropagateOnce.
+    wxEvtHandler*     m_propagatedFrom;
+
     bool              m_skipped;
     bool              m_isCommandEvent;
 
@@ -1075,7 +1082,7 @@ protected:
     wxEvent& operator=(const wxEvent&); // for derived classes operator=()
 
 private:
-    // it needs to access our m_propagationLevel
+    // It needs to access our m_propagationLevel and m_propagatedFrom fields.
     friend class WXDLLIMPEXP_FWD_BASE wxPropagateOnce;
 
     // and this one needs to access our m_handlerToProcessOnlyIn
@@ -1109,26 +1116,35 @@ private:
 };
 
 /*
- * Another one to temporarily lower propagation level.
+ * Helper used to indicate that an event is propagated upwards the window
+ * hierarchy by the given window.
  */
 class WXDLLIMPEXP_BASE wxPropagateOnce
 {
 public:
-    wxPropagateOnce(wxEvent& event) : m_event(event)
+    // The handler argument should normally be non-NULL to allow the parent
+    // event handler to know that it's being used to process an event coming
+    // from the child, it's only NULL by default for backwards compatibility.
+    wxPropagateOnce(wxEvent& event, wxEvtHandler* handler = NULL)
+        : m_event(event),
+          m_propagatedFromOld(event.m_propagatedFrom)
     {
         wxASSERT_MSG( m_event.m_propagationLevel > 0,
                         wxT("shouldn't be used unless ShouldPropagate()!") );
 
         m_event.m_propagationLevel--;
+        m_event.m_propagatedFrom = handler;
     }
 
     ~wxPropagateOnce()
     {
+        m_event.m_propagatedFrom = m_propagatedFromOld;
         m_event.m_propagationLevel++;
     }
 
 private:
     wxEvent& m_event;
+    wxEvtHandler* const m_propagatedFromOld;
 
     wxDECLARE_NO_COPY_CLASS(wxPropagateOnce);
 };
diff --git a/include/wx/mdi.h b/include/wx/mdi.h
index 78c2f4c3e4..52cbed7da4 100644
--- a/include/wx/mdi.h
+++ b/include/wx/mdi.h
@@ -381,8 +381,18 @@ inline bool wxMDIParentFrameBase::TryBefore(wxEvent& event)
             event.GetEventType() == wxEVT_UPDATE_UI )
     {
         wxMDIChildFrame * const child = GetActiveChild();
-        if ( child && child->ProcessWindowEventLocally(event) )
-            return true;
+        if ( child )
+        {
+            // However avoid sending the event back to the child if it's
+            // currently being propagated to us from it.
+            wxWindow* const
+                from = static_cast<wxWindow*>(event.GetPropagatedFrom());
+            if ( !from || !from->IsDescendant(child) )
+            {
+                if ( child->ProcessWindowEventLocally(event) )
+                    return true;
+            }
+        }
     }
 
     return wxFrame::TryBefore(event);
diff --git a/src/common/event.cpp b/src/common/event.cpp
index 2733450352..c9e8f11bca 100644
--- a/src/common/event.cpp
+++ b/src/common/event.cpp
@@ -369,6 +369,7 @@ wxEvent::wxEvent(int theId, wxEventType commandType)
     m_handlerToProcessOnlyIn = NULL;
     m_isCommandEvent = false;
     m_propagationLevel = wxEVENT_PROPAGATE_NONE;
+    m_propagatedFrom = NULL;
     m_wasProcessed = false;
     m_willBeProcessedAgain = false;
 }
@@ -382,6 +383,7 @@ wxEvent::wxEvent(const wxEvent& src)
     , m_callbackUserData(src.m_callbackUserData)
     , m_handlerToProcessOnlyIn(NULL)
     , m_propagationLevel(src.m_propagationLevel)
+    , m_propagatedFrom(NULL)
     , m_skipped(src.m_skipped)
     , m_isCommandEvent(src.m_isCommandEvent)
     , m_wasProcessed(false)
@@ -400,6 +402,7 @@ wxEvent& wxEvent::operator=(const wxEvent& src)
     m_callbackUserData = src.m_callbackUserData;
     m_handlerToProcessOnlyIn = NULL;
     m_propagationLevel = src.m_propagationLevel;
+    m_propagatedFrom = NULL;
     m_skipped = src.m_skipped;
     m_isCommandEvent = src.m_isCommandEvent;
 
diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp
index 3c2c0f4e1d..787d08b13f 100644
--- a/src/common/wincmn.cpp
+++ b/src/common/wincmn.cpp
@@ -3370,7 +3370,7 @@ bool wxWindowBase::TryAfter(wxEvent& event)
             wxWindow *parent = GetParent();
             if ( parent && !parent->IsBeingDeleted() )
             {
-                wxPropagateOnce propagateOnce(event);
+                wxPropagateOnce propagateOnce(event, this);
 
                 return parent->GetEventHandler()->ProcessEvent(event);
             }
-- 
2.47.2