return false;
}
+ // This is also used only internally by ProcessEvent() to check if it
+ // should process the event normally or only restrict the search for the
+ // event handler to this object itself.
+ bool ShouldProcessHereOnly() const { return m_processHereOnly; }
+
protected:
wxObject* m_eventObject;
wxEventType m_eventType;
// once for this event
bool m_wasProcessed;
+ // this flag is used by ProcessEventLocally() to prevent ProcessEvent()
+ // from doing its usual stuff and force it to just call ProcessEventHere()
+ // instead, see the comment there explaining why is this needed
+ bool m_processHereOnly;
+
protected:
wxEvent(const wxEvent&); // for implementing Clone()
wxEvent& operator=(const wxEvent&); // for derived classes operator=()
// it needs to access our m_propagationLevel
friend class WXDLLIMPEXP_FWD_BASE wxPropagateOnce;
+ // and this one needs to access our m_processHereOnly
+ friend class WXDLLIMPEXP_FWD_BASE wxEventProcessHereOnly;
+
+
DECLARE_ABSTRACT_CLASS(wxEvent)
};
wxDECLARE_NO_COPY_CLASS(wxPropagateOnce);
};
+// A helper used by ProcessEventLocally() to restrict the event processing
+// to this handler only.
+class WXDLLIMPEXP_BASE wxEventProcessHereOnly
+{
+public:
+ wxEventProcessHereOnly(wxEvent& event) : m_event(event)
+ {
+ // This would be unexpected and would also restore the wrong value in
+ // this class dtor so if even does happen legitimately we'd need to
+ // store the value in ctor and restore it in dtor.
+ wxASSERT_MSG( !m_event.m_processHereOnly,
+ "shouldn't be used twice for the same event" );
+
+ m_event.m_processHereOnly = true;
+ }
+
+ ~wxEventProcessHereOnly()
+ {
+ m_event.m_processHereOnly = false;
+ }
+
+private:
+ wxEvent& m_event;
+
+ wxDECLARE_NO_COPY_CLASS(wxEventProcessHereOnly);
+};
#if wxUSE_GUI
bool SafelyProcessEvent(wxEvent& event);
// NOTE: uses ProcessEvent()
+ // This method tries to process the event in this event handler, including
+ // any preprocessing done by TryBefore() and all the handlers chained to
+ // it, but excluding the post-processing done in TryAfter().
+ //
+ // It is meant to be called from ProcessEvent() only and is not virtual,
+ // additional event handlers can be hooked into the normal event processing
+ // logic using TryBefore() and TryAfter() hooks.
+ //
+ // You can also call it yourself to forward an event to another handler but
+ // without propagating it upwards if it's unhandled (this is usually
+ // unwanted when forwarding as the original handler would already do it if
+ // needed normally).
+ bool ProcessEventLocally(wxEvent& event);
+
// Schedule the given event to be processed later. It takes ownership of
// the event pointer, i.e. it will be deleted later. This is safe to call
// from multiple threads although you still need to ensure that wxString
// The method tries to process the event in this event handler.
//
- // It is meant to be called from ProcessEvent() only and is not virtual,
- // additional event handlers can be hooked into the normal event processing
- // logic using TryBefore() and TryAfter() hooks.
+ // It is called from ProcessEventLocally() and normally shouldn't be called
+ // directly as doing it would ignore any chained event handlers.
bool ProcessEventHere(wxEvent& event);
// pass the event to wxTheApp instance, called from TryAfter()
bool DoTryApp(wxEvent& event);
+ // try to process events in all handlers chained to this one
+ bool DoTryChain(wxEvent& event);
+
DECLARE_DYNAMIC_CLASS_NO_COPY(wxEvtHandler)
};
processed, ProcessEvent() on wxTheApp object is called as the last
step.
- Notice that steps (3)-(5) are performed in ProcessEventHere() which is
- called by this function.
+ Notice that steps (2)-(6) are performed in ProcessEventLocally()
+ which is called by this function.
@param event
Event to process.
*/
virtual bool ProcessEvent(wxEvent& event);
+ /**
+ Try to process the event in this handler and all those chained to it.
+
+ As explained in ProcessEvent() documentation, the event handlers may be
+ chained in a doubly-linked list. This function tries to process the
+ event in this handler (including performing any pre-processing done in
+ TryBefore(), e.g. applying validators) and all those following it in
+ the chain until the event is processed or the chain is exhausted.
+
+ This function is called from ProcessEvent() and, in turn, calls
+ ProcessEventHere() for each handler in turn. It is not virtual and so
+ cannot be overridden but can, and should, be called to forward an event
+ to another handler instead of ProcessEvent() which would result in a
+ duplicate call to TryAfter(), e.g. resulting in all unprocessed events
+ being sent to the application object multiple times.
+
+ @since 2.9.1
+
+ @param event
+ Event to process.
+ @return
+ @true if this handler of one of those chained to it processed the
+ event.
+ */
+ bool ProcessEventLocally(wxEvent& event);
+
/**
Try to process the event in this event handler.
- This method is called from ProcessEvent(), please see the detailed
- description of the event processing logic there.
+ This method is called from ProcessEventLocally() and thus,
+ indirectly, from ProcessEvent(), please see the detailed description of
+ the event processing logic there.
It is @em not virtual and so may not be overridden.
+ @since 2.9.1
+
@param event
Event to process.
@return
m_isCommandEvent = false;
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
m_wasProcessed = false;
+ m_processHereOnly = false;
}
wxEvent::wxEvent(const wxEvent& src)
, m_skipped(src.m_skipped)
, m_isCommandEvent(src.m_isCommandEvent)
, m_wasProcessed(false)
+ , m_processHereOnly(false)
{
}
m_skipped = src.m_skipped;
m_isCommandEvent = src.m_isCommandEvent;
- // don't change m_wasProcessed
+ // don't change m_wasProcessed nor m_processHereOnly
return *this;
}
bool wxEvtHandler::TryAfter(wxEvent& event)
{
+ // We only want to pass the window to the application object once even if
+ // there are several chained handlers. Ensure that this is what happens by
+ // only calling DoTryApp() if there is no next handler (which would do it).
+ //
+ // Notice that, unlike simply calling TryAfter() on the last handler in the
+ // chain only from ProcessEvent(), this also works with wxWindow object in
+ // the middle of the chain: its overridden TryAfter() will still be called
+ // and propagate the event upwards the window hierarchy even if it's not
+ // the last one in the chain (which, admittedly, shouldn't happen often).
+ if ( GetNextHandler() )
+ return GetNextHandler()->TryAfter(event);
+
#if WXWIN_COMPATIBILITY_2_8
// as above, call the old virtual function for compatibility
return TryParent(event);
bool wxEvtHandler::ProcessEvent(wxEvent& event)
{
- // allow the application to hook into event processing
+ // The very first thing we do is to allow the application to hook into
+ // event processing in order to globally pre-process all events.
//
- // note that we should only do it if we're the first event handler called
+ // Note that we should only do it if we're the first event handler called
// to avoid calling FilterEvent() multiple times as the event goes through
- // the event handler chain and possibly upwards the window hierarchy
+ // the event handler chain and possibly upwards the window hierarchy.
if ( !event.WasProcessed() )
{
if ( wxTheApp )
}
}
- // Try the hooks which should be called before our own handlers
+ // Short circuit the event processing logic if we're requested to process
+ // this event in this handler only, see DoTryChain() for more details.
+ if ( event.ShouldProcessHereOnly() )
+ return ProcessEventHere(event);
+
+
+ // Try to process the event in this handler itself.
+ if ( ProcessEventLocally(event) )
+ return true;
+
+ // If we still didn't find a handler, propagate the event upwards the
+ // window chain and/or to the application object.
+ if ( TryAfter(event) )
+ return true;
+
+
+ // No handler found anywhere, bail out.
+ return false;
+}
+
+bool wxEvtHandler::ProcessEventLocally(wxEvent& event)
+{
+ // First try the hooks which should be called before our own handlers
if ( TryBefore(event) )
return true;
+ // Then try this handler itself, notice that we should not call
+ // ProcessEvent() on this one as we're already called from it, which
+ // explains why we do it here and not in DoTryChain()
if ( ProcessEventHere(event) )
return true;
- // pass the event to the next handler, notice that we shouldn't call
- // TryAfter() even if it doesn't handle the event as the last handler in
- // the chain will do it
- if ( GetNextHandler() )
- return GetNextHandler()->ProcessEvent(event);
+ // Finally try the event handlers chained to this one,
+ if ( DoTryChain(event) )
+ return true;
+
+ // And return false to indicate that we didn't find any handler at this
+ // level.
+ return false;
+}
- // propagate the event upwards the window chain and/or to the application
- // object if it wasn't processed at this level
- return TryAfter(event);
+bool wxEvtHandler::DoTryChain(wxEvent& event)
+{
+ for ( wxEvtHandler *h = GetNextHandler(); h; h = h->GetNextHandler() )
+ {
+ // We need to process this event at the level of this handler only
+ // right now, the pre-/post-processing was either already done by
+ // ProcessEvent() from which we were called or will be done by it when
+ // we return.
+ //
+ // However we must call ProcessEvent() and not ProcessEventHere()
+ // because the existing code (including some in wxWidgets itself)
+ // expects the overridden ProcessEvent() in its custom event handlers
+ // pushed on a window to be called.
+ //
+ // So we must call ProcessEvent() but it must not do what it usually
+ // does. To resolve this paradox we pass a special "process here only"
+ // flag to ProcessEvent() via the event object itself. This ensures
+ // that if our own, base class, version is called, it will just call
+ // ProcessEventHere() and won't do anything else, just as we want it to.
+ wxEventProcessHereOnly processHereOnly(event);
+ if ( h->ProcessEvent(event) )
+ return true;
+ }
+
+ return false;
}
bool wxEvtHandler::ProcessEventHere(wxEvent& event)