and the possibilities of handling events in this way are rather different.\r
\r
Let us start by looking at the syntax: the first obvious difference is that you\r
-need not use @c DECLARE_EVENT_TABLE() nor @c BEGIN_EVENT_TABLE and the\r
+need not use DECLARE_EVENT_TABLE() nor BEGIN_EVENT_TABLE() and the\r
associated macros. Instead, in any place in your code, but usually in\r
the code of the class defining the handler itself (and definitely not in the\r
global scope as with the event tables), call its Connect() method like this:\r
<li value="5">\r
The event is passed to the next event handler, if any, in the event handler\r
chain, i.e., the steps (1) to (4) are done for it. This chain can be formed\r
- using wxEvtHandler::SetNextHandler() or wxWindow::PushEventHandler() but\r
- usually there is no next event handler and chaining event handlers using\r
- these functions is much less useful now that Connect() exists so this step\r
- will almost never do anything.\r
+ using wxEvtHandler::SetNextHandler():\r
+ @image html overview_eventhandling_chain.png\r
+ (referring to the image, if @c A->ProcessEvent is called and it doesn't handle\r
+ the event, @c B->ProcessEvent will be called and so on...).\r
+ In the case of wxWindow you can build a stack (implemented using wxEvtHandler\r
+ double-linked list) using wxWindow::PushEventHandler():\r
+ @image html overview_eventhandling_winstack.png\r
+ (referring to the image, if @c W->ProcessEvent is called, it immediately calls\r
+ @c A->ProcessEvent; if nor @c A nor @c B handle the event, then the wxWindow\r
+ itself is used - i.e. the dynamically connected event handlers and static\r
+ event table entries of wxWindow are looked as the last possibility, after\r
+ all pushed event handlers were tested).\r
+ Note however that usually there are no wxEvtHandler chains nor wxWindows stacks\r
+ so this step will usually do anything.\r
</li>\r
\r
<li value="6">\r
\r
<li value="7">\r
Finally, i.e., if the event is still not processed, the wxApp object itself\r
- gets a last chance to process it.\r
+ (which derives from wxEvtHandler) gets a last chance to process it.\r
</li>\r
</ol>\r
\r
}
.memproto, .memdoc {
- border: 1px solid #84b0c7;
+ border: 1px solid #84b0c7;
}
.memproto {
color: black;
}
+IMG {
+ margin: 20px;
+}
+
IMG.logo {
float: right;
margin: 20px;
wxEvtHandler();
virtual ~wxEvtHandler();
+
+ // Event handler chain
+ // -------------------
+
wxEvtHandler *GetNextHandler() const { return m_nextHandler; }
wxEvtHandler *GetPreviousHandler() const { return m_previousHandler; }
- void SetNextHandler(wxEvtHandler *handler) { m_nextHandler = handler; }
- void SetPreviousHandler(wxEvtHandler *handler) { m_previousHandler = handler; }
+ virtual void SetNextHandler(wxEvtHandler *handler) { m_nextHandler = handler; }
+ virtual void SetPreviousHandler(wxEvtHandler *handler) { m_previousHandler = handler; }
void SetEvtHandlerEnabled(bool enabled) { m_enabled = enabled; }
bool GetEvtHandlerEnabled() const { return m_enabled; }
+ void Unlink();
+ bool IsUnlinked() const;
+
+
+
+ // Event queuing and processing
+ // ----------------------------
+
+
// Process an event right now: this can only be called from the main
// thread, use QueueEvent() for scheduling the events for
// processing from other threads.
// when called from C code (e.g. in GTK+ callback) when the exception
// wouldn't correctly propagate to wxEventLoop.
bool SafelyProcessEvent(wxEvent& event);
+ // NOTE: uses ProcessEvent()
// 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
}
void ProcessPendingEvents();
+ // NOTE: uses ProcessEvent()
#if wxUSE_THREADS
bool ProcessThreadEvent(const wxEvent& event);
+ // NOTE: uses AddPendingEvent()
#endif
+
+ // Connecting and disconnecting
+ // ----------------------------
+
// Dynamic association of a member function handler with the event handler,
// winid and event type
void Connect(int winid,
// replace the event handler (allows to completely subclass the
// window)
- void SetEventHandler( wxEvtHandler *handler ) { m_eventHandler = handler; }
+ void SetEventHandler( wxEvtHandler *handler );
// push/pop event handler: allows to chain a custom event handler to
// alreasy existing ones
// be there)
bool RemoveEventHandler(wxEvtHandler *handler);
- // Process an event by calling GetEventHandler()->ProcessEvent() and
- // handling any exceptions thrown by event handlers. It's mostly useful
- // when processing wx events when called from C code (e.g. in GTK+
- // callback) when the exception wouldn't correctly propagate to
- // wxEventLoop.
+ // Process an event by calling GetEventHandler()->ProcessEvent() and
+ // handling any exceptions thrown by event handlers. It's mostly useful
+ // when processing wx events when called from C code (e.g. in GTK+
+ // callback) when the exception wouldn't correctly propagate to
+ // wxEventLoop.
bool HandleWindowEvent(wxEvent& event) const;
+ // disable wxEvtHandler double-linked list mechanism:
+ virtual void SetNextHandler(wxEvtHandler *handler);
+ virtual void SetPreviousHandler(wxEvtHandler *handler);
+
// validators
// ----------
wxWindow is (and therefore all window classes are) derived from this class.
When events are received, wxEvtHandler invokes the method listed in the
- event table using itself as the object. When using multiple inheritance
+ event table using itself as the object. When using multiple inheritance
<b>it is imperative that the wxEvtHandler(-derived) class is the first
class inherited</b> such that the @c this pointer for the overall object
will be identical to the @c this pointer of the wxEvtHandler portion.
/**
Destructor.
- If the handler is part of a chain, the destructor will unlink itself and
- restore the previous and next handlers so that they point to each other.
+ If the handler is part of a chain, the destructor will unlink itself
+ (see Unlink()).
*/
virtual ~wxEvtHandler();
The normal order of event table searching is as follows:
-# If the object is disabled (via a call to wxEvtHandler::SetEvtHandlerEnabled)
- the function skips to step (6).
+ the function skips to step (6).
-# If the object is a wxWindow, ProcessEvent() is recursively called on the
- window's wxValidator. If this returns @true, the function exits.
+ window's wxValidator. If this returns @true, the function exits.
-# SearchEventTable() is called for this event handler. If this fails, the base
- class table is tried, and so on until no more tables exist or an appropriate
- function was found, in which case the function exits.
+ class table is tried, and so on until no more tables exist or an appropriate
+ function was found, in which case the function exits.
-# The search is applied down the entire chain of event handlers (usually the
- chain has a length of one). If this succeeds, the function exits.
+ chain has a length of one). This chain can be formed using wxEvtHandler::SetNextHandler():
+ @image html overview_eventhandling_chain.png
+ (referring to the image, if @c A->ProcessEvent is called and it doesn't handle
+ the event, @c B->ProcessEvent will be called and so on...).
+ Note that in the case of wxWindow you can build a stack of event handlers
+ (see wxWindow::PushEventHandler() for more info).
+ If any of the handlers of the chain return @true, the function exits.
-# If the object is a wxWindow and the event is a wxCommandEvent, ProcessEvent()
- is recursively applied to the parent window's event handler.
- If this returns true, the function exits.
+ is recursively applied to the parent window's event handler.
+ If this returns @true, the function exits.
-# Finally, ProcessEvent() is called on the wxApp object.
@param event
/**
- @name Event handler chain
+ @name Event handler chaining
+
+ wxEvtHandler can be arranged in a double-linked list of handlers
+ which is automatically iterated by ProcessEvent() if needed.
*/
//@{
/**
Sets the pointer to the next handler.
+ @remarks
+ See ProcessEvent() for more info about how the chains of event handlers
+ are internally used.
+ Also remember that wxEvtHandler uses double-linked lists and thus if you
+ use this function, you should also call SetPreviousHandler() on the
+ argument passed to this function:
+ @code
+ handlerA->SetNextHandler(handlerB);
+ handlerB->SetPreviousHandler(handlerA);
+ @endcode
+
@param handler
- Event handler to be set as the next handler.
+ The event handler to be set as the next handler.
+ Cannot be @NULL.
- @see GetNextHandler(), SetPreviousHandler(), GetPreviousHandler(),
- wxWindow::PushEventHandler, wxWindow::PopEventHandler
+ @see @ref overview_eventhandling_processing
*/
- void SetNextHandler(wxEvtHandler* handler);
+ virtual void SetNextHandler(wxEvtHandler* handler);
/**
Sets the pointer to the previous handler.
+ All remarks about SetNextHandler() apply to this function as well.
@param handler
- Event handler to be set as the previous handler.
+ The event handler to be set as the previous handler.
+ Cannot be @NULL.
+
+ @see @ref overview_eventhandling_processing
+ */
+ virtual void SetPreviousHandler(wxEvtHandler* handler);
+
+ /**
+ Unlinks this event handler from the chain it's part of (if any);
+ then links the "previous" event handler to the "next" one
+ (so that the chain won't be interrupted).
+
+ E.g. if before calling Unlink() you have the following chain:
+ @image html evthandler_unlink_before.png
+ then after calling @c B->Unlink() you'll have:
+ @image html evthandler_unlink_after.png
+
+ @since 2.9.0
+ */
+ void Unlink();
+
+ /**
+ Returns @true if the next and the previous handler pointers of this
+ event handler instance are @NULL.
+
+ @since 2.9.0
+
+ @see SetPreviousHandler(), SetNextHandler()
*/
- void SetPreviousHandler(wxEvtHandler* handler);
+ bool IsUnlinked() const;
//@}
};
/**
@name Event-handling functions
+
+ wxWindow allows you to build a (sort of) stack of event handlers which
+ can be used to override the window's own event handling.
*/
//@{
wxEvtHandler* GetEventHandler() const;
/**
- This function will generate the appropriate call to
- Navigate() if the key event is one normally used for
- keyboard navigation and return @true in this case.
+ This function will generate the appropriate call to Navigate() if the key
+ event is one normally used for keyboard navigation and return @true in this case.
@return Returns @true if the key pressed was for navigation and was
handled, @false otherwise.
/**
Removes and returns the top-most event handler on the event handler stack.
+ E.g. in the case of:
+ @image html overview_eventhandling_winstack.png
+ when calling @c W->PopEventHandler(), the event handler @c A will be
+ removed and @c B will be the first handler of the stack.
+
+ Note that it's an error to call this function when no event handlers
+ were pushed on this window (i.e. when the window itself is its only
+ event handler).
+
@param deleteHandler
- If this is @true, the handler will be deleted after it is removed.
- The default value is @false.
+ If this is @true, the handler will be deleted after it is removed
+ (and the returned value will be @NULL).
- @see SetEventHandler(), GetEventHandler(),
- PushEventHandler(), wxEvtHandler::ProcessEvent, wxEvtHandler
+ @see @ref overview_eventhandling_processing
*/
wxEvtHandler* PopEventHandler(bool deleteHandler = false);
/**
Pushes this event handler onto the event stack for the window.
+ An event handler is an object that is capable of processing the events sent
+ to a window. By default, the window is its own event handler, but an application
+ may wish to substitute another, for example to allow central implementation
+ of event-handling for a variety of different window classes.
+
+ wxWindow::PushEventHandler allows an application to set up a @e stack
+ of event handlers, where an event not handled by one event handler is
+ handed to the next one in the chain.
+
+ E.g. if you have two event handlers @c A and @c B and a wxWindow instance
+ @c W and you call:
+ @code
+ W->PushEventHandler(A);
+ W->PushEventHandler(B);
+ @endcode
+ you will end up with the following situation:
+ @image html overview_eventhandling_winstack.png
+
+ Note that you can use wxWindow::PopEventHandler to remove the event handler.
+
@param handler
Specifies the handler to be pushed.
+ It must not be part of a wxEvtHandler chain; an assert will fail
+ if it's not unlinked (see wxEvtHandler::IsUnlinked).
- @remarks An event handler is an object that is capable of processing the
- events sent to a window. By default, the window is its
- own event handler, but an application may wish to
- substitute another, for example to allow central
- implementation of event-handling for a variety of
- different window classes.
- wxWindow::PushEventHandler allows an application to set up a
- chain of event handlers, where an event not handled by one event
- handler is handed to the next one in the chain.
- Use wxWindow::PopEventHandler to remove the event handler.
-
- @see SetEventHandler(), GetEventHandler(),
- PopEventHandler(), wxEvtHandler::ProcessEvent, wxEvtHandler
+ @see @ref overview_eventhandling_processing
*/
void PushEventHandler(wxEvtHandler* handler);
/**
- Find the given @a handler in the windows event handler chain and remove
- (but not delete) it from it.
+ Find the given @a handler in the windows event handler stack and unlinks
+ (but not delete) it. See wxEvtHandler::Unlink() for more info.
@param handler
The event handler to remove, must be non-@NULL and
- must be present in this windows event handlers chain
+ must be present in this windows event handlers stack.
@return Returns @true if it was found and @false otherwise (this also
results in an assert failure so this function should
/**
Sets the event handler for this window.
+ Note that if you use this function you may want to use as the "next" handler
+ of @a handler the window itself; in this way when @a handler doesn't process
+ an event, the window itself will have a chance to do it.
+
@param handler
- Specifies the handler to be set.
-
- @remarks An event handler is an object that is capable of processing the
- events sent to a window. By default, the window is its
- own event handler, but an application may wish to
- substitute another, for example to allow central
- implementation of event-handling for a variety of
- different window classes.
- It is usually better to use wxWindow::PushEventHandler since
- this sets up a chain of event handlers, where an event not
- handled by one event handler is handed to the next one in the chain.
-
- @see GetEventHandler(), PushEventHandler(),
- PopEventHandler(), wxEvtHandler::ProcessEvent, wxEvtHandler
+ Specifies the handler to be set. Cannot be @NULL.
+
+ @see @ref overview_eventhandling_processing
*/
void SetEventHandler(wxEvtHandler* handler);
+ /**
+ wxWindows cannot be used to form event handler chains; this function
+ thus will assert when called.
+
+ Note that instead you can use PushEventHandler() or SetEventHandler() to
+ implement a stack of event handlers to override wxWindow's own
+ event handling mechanism.
+ */
+ virtual void SetNextHandler(wxEvtHandler* handler);
+
+ /**
+ wxWindows cannot be used to form event handler chains; this function
+ thus will assert when called.
+
+ Note that instead you can use PushEventHandler() or SetEventHandler() to
+ implement a stack of event handlers to override wxWindow's own
+ event handling mechanism.
+ */
+ virtual void SetPreviousHandler(wxEvtHandler* handler);
+
//@}
+
/**
@name Window styles functions
*/
wxEvtHandler::~wxEvtHandler()
{
- // Takes itself out of the list of handlers
- if (m_previousHandler)
- m_previousHandler->m_nextHandler = m_nextHandler;
-
- if (m_nextHandler)
- m_nextHandler->m_previousHandler = m_previousHandler;
+ Unlink();
if (m_dynamicEvents)
{
delete m_clientObject;
}
+void wxEvtHandler::Unlink()
+{
+ // this event handler must take itself out of the chain of handlers:
+
+ if (m_previousHandler)
+ m_previousHandler->SetNextHandler(m_nextHandler);
+
+ if (m_nextHandler)
+ m_nextHandler->SetPreviousHandler(m_previousHandler);
+
+ m_nextHandler = NULL;
+ m_previousHandler = NULL;
+}
+
+bool wxEvtHandler::IsUnlinked() const
+{
+ return m_previousHandler == NULL &&
+ m_nextHandler == NULL;
+}
+
#if wxUSE_THREADS
bool wxEvtHandler::ProcessThreadEvent(const wxEvent& event)
// event handler stuff
// ----------------------------------------------------------------------------
-void wxWindowBase::PushEventHandler(wxEvtHandler *handler)
+void wxWindowBase::SetEventHandler(wxEvtHandler *handler)
{
+ wxCHECK_RET(handler != NULL, "SetEventHandler(NULL) called");
+
+ m_eventHandler = handler;
+}
+
+void wxWindowBase::SetNextHandler(wxEvtHandler *WXUNUSED(handler))
+{
+ // disable wxEvtHandler chain mechanism for wxWindows:
+ // wxWindow uses its own stack mechanism which doesn't mix well with wxEvtHandler's one
+
+ wxFAIL_MSG("wxWindow cannot be part of a wxEvtHandler chain");
+}
+void wxWindowBase::SetPreviousHandler(wxEvtHandler *WXUNUSED(handler))
+{
+ // we can't simply wxFAIL here as in SetNextHandler: in fact the last
+ // handler of our stack when is destroyed will be Unlink()ed and thus
+ // will call this function to update the pointer of this window...
+
+ //wxFAIL_MSG("wxWindow cannot be part of a wxEvtHandler chain");
+}
+
+void wxWindowBase::PushEventHandler(wxEvtHandler *handlerToPush)
+{
+ wxCHECK_RET( handlerToPush != NULL, "PushEventHandler(NULL) called" );
+
+ // the new handler is going to be part of the wxWindow stack of event handlers:
+ // it can't be part also of an event handler double-linked chain:
+ wxASSERT_MSG(handlerToPush->IsUnlinked(),
+ "The handler being pushed in the wxWindow stack shouldn't be part of "
+ "a wxEvtHandler chain; call Unlink() on it first");
+
wxEvtHandler *handlerOld = GetEventHandler();
+ wxCHECK_RET( handlerOld, "an old event handler is NULL?" );
+
+ // now use wxEvtHandler double-linked list to implement a stack:
+ handlerToPush->SetNextHandler(handlerOld);
- handler->SetNextHandler(handlerOld);
+ if (handlerOld != this)
+ handlerOld->SetPreviousHandler(handlerToPush);
- if ( handlerOld )
- GetEventHandler()->SetPreviousHandler(handler);
+ SetEventHandler(handlerToPush);
- SetEventHandler(handler);
+#ifdef __WXDEBUG__
+ // final checks of the operations done above:
+ wxASSERT_MSG( handlerToPush->GetPreviousHandler() == NULL,
+ "the first handler of the wxWindow stack should have no previous handlers set" );
+ wxASSERT_MSG( handlerToPush->GetNextHandler() != NULL,
+ "the first handler of the wxWindow stack should have non-NULL next handler" );
+
+ wxEvtHandler* pLast = handlerToPush;
+ while (pLast && pLast != this)
+ pLast = pLast->GetNextHandler();
+ wxASSERT_MSG( pLast->GetNextHandler() == NULL,
+ "the last handler of the wxWindow stack should have this window as next handler" );
+#endif
}
wxEvtHandler *wxWindowBase::PopEventHandler(bool deleteHandler)
{
- wxEvtHandler *handlerA = GetEventHandler();
- if ( handlerA )
- {
- wxEvtHandler *handlerB = handlerA->GetNextHandler();
- handlerA->SetNextHandler(NULL);
+ // we need to pop the wxWindow stack, i.e. we need to remove the first handler
- if ( handlerB )
- handlerB->SetPreviousHandler(NULL);
- SetEventHandler(handlerB);
+ wxEvtHandler *firstHandler = GetEventHandler();
+ wxCHECK_MSG( firstHandler != NULL, NULL, "wxWindow cannot have a NULL event handler" );
+ wxCHECK_MSG( firstHandler != this, NULL, "cannot pop the wxWindow itself" );
+ wxCHECK_MSG( firstHandler->GetPreviousHandler() == NULL, NULL,
+ "the first handler of the wxWindow stack should have no previous handlers set" );
- if ( deleteHandler )
- {
- delete handlerA;
- handlerA = NULL;
- }
+ wxEvtHandler *secondHandler = firstHandler->GetNextHandler();
+ wxCHECK_MSG( secondHandler != NULL, NULL,
+ "the first handler of the wxWindow stack should have non-NULL next handler" );
+
+ firstHandler->SetNextHandler(NULL);
+ secondHandler->SetPreviousHandler(NULL);
+
+ // now firstHandler is completely unlinked; set secondHandler as the new window event handler
+ SetEventHandler(secondHandler);
+
+ if ( deleteHandler )
+ {
+ delete firstHandler;
+ firstHandler = NULL;
}
- return handlerA;
+ return firstHandler;
}
-bool wxWindowBase::RemoveEventHandler(wxEvtHandler *handler)
+bool wxWindowBase::RemoveEventHandler(wxEvtHandler *handlerToRemove)
{
- wxCHECK_MSG( handler, false, _T("RemoveEventHandler(NULL) called") );
+ wxCHECK_MSG( handlerToRemove != NULL, false, "RemoveEventHandler(NULL) called" );
+ wxCHECK_MSG( handlerToRemove != this, false, "Cannot remove the window itself" );
+
+ if (handlerToRemove == GetEventHandler())
+ {
+ // removing the first event handler is equivalent to "popping" the stack
+ PopEventHandler(false);
+ return true;
+ }
- wxEvtHandler *handlerPrev = NULL,
- *handlerCur = GetEventHandler();
- while ( handlerCur )
+ // NOTE: the wxWindow event handler list is always terminated with "this" handler
+ wxEvtHandler *handlerCur = GetEventHandler()->GetNextHandler();
+ while ( handlerCur != this )
{
wxEvtHandler *handlerNext = handlerCur->GetNextHandler();
- if ( handlerCur == handler )
+ if ( handlerCur == handlerToRemove )
{
- if ( handlerPrev )
- {
- handlerPrev->SetNextHandler(handlerNext);
- }
- else
- {
- SetEventHandler(handlerNext);
- }
-
- if ( handlerNext )
- {
- handlerNext->SetPreviousHandler ( handlerPrev );
- }
-
- handler->SetNextHandler(NULL);
- handler->SetPreviousHandler(NULL);
+ handlerCur->Unlink();
+ wxASSERT_MSG( handlerCur != GetEventHandler(),
+ "the case Remove == Pop should was already handled" );
return true;
}
- handlerPrev = handlerCur;
handlerCur = handlerNext;
}
bool wxWindowBase::HandleWindowEvent(wxEvent& event) const
{
+ // SafelyProcessEvent() will handle exceptions nicely
return GetEventHandler()->SafelyProcessEvent(event);
}