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
This commit is contained in:
Vadim Zeitlin 2013-07-03 22:18:42 +00:00
parent 12ab6ad3d2
commit 5ac5e40e41
4 changed files with 35 additions and 6 deletions

View File

@ -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);
};

View File

@ -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);

View File

@ -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;

View File

@ -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);
}