// Author: Julian Smart
// Modified by:
// Created: 01/02/97
-// RCS-ID: $Id$
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#endif
#include "wx/event.h"
+#include "wx/eventfilter.h"
#include "wx/evtloop.h"
#ifndef WX_PRECOMP
IMPLEMENT_DYNAMIC_CLASS(wxEvtHandler, wxObject)
IMPLEMENT_ABSTRACT_CLASS(wxEvent, wxObject)
IMPLEMENT_DYNAMIC_CLASS(wxIdleEvent, wxEvent)
+ IMPLEMENT_DYNAMIC_CLASS(wxThreadEvent, wxEvent)
#endif // wxUSE_BASE
#if wxUSE_GUI
IMPLEMENT_DYNAMIC_CLASS(wxCommandEvent, wxEvent)
- IMPLEMENT_DYNAMIC_CLASS(wxThreadEvent, wxCommandEvent)
IMPLEMENT_DYNAMIC_CLASS(wxNotifyEvent, wxCommandEvent)
IMPLEMENT_DYNAMIC_CLASS(wxScrollEvent, wxCommandEvent)
IMPLEMENT_DYNAMIC_CLASS(wxScrollWinEvent, wxEvent)
wxEventHashTable wxEvtHandler::sm_eventHashTable(wxEvtHandler::sm_eventTable);
const wxEventTableEntry wxEvtHandler::sm_eventTableEntries[] =
- { DECLARE_EVENT_TABLE_TERMINATOR() };
+ { wxDECLARE_EVENT_TABLE_TERMINATOR() };
// wxUSE_MEMORY_TRACING considers memory freed from the static objects dtors
const wxEventType wxEVT_FIRST = 10000;
const wxEventType wxEVT_USER_FIRST = wxEVT_FIRST + 2000;
+const wxEventType wxEVT_NULL = wxNewEventType();
-DEFINE_EVENT_TYPE(wxEVT_NULL)
wxDEFINE_EVENT( wxEVT_IDLE, wxIdleEvent );
+// Thread and asynchronous call events
+wxDEFINE_EVENT( wxEVT_THREAD, wxThreadEvent );
+wxDEFINE_EVENT( wxEVT_ASYNC_METHOD_CALL, wxAsyncMethodCallEvent );
+
#endif // wxUSE_BASE
#if wxUSE_GUI
-wxDEFINE_EVENT( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_MENU_SELECTED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_SLIDER_UPDATED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_SCROLLBAR_UPDATED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_VLBOX_SELECTED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_TOOL_ENTER, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED, wxCommandEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_COMBOBOX_DROPDOWN, wxCommandEvent);
-wxDEFINE_EVENT( wxEVT_COMMAND_COMBOBOX_CLOSEUP, wxCommandEvent);
+wxDEFINE_EVENT( wxEVT_BUTTON, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_CHECKBOX, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_CHOICE, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_LISTBOX, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_LISTBOX_DCLICK, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_CHECKLISTBOX, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_MENU, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_SLIDER, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_RADIOBOX, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_RADIOBUTTON, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_SCROLLBAR, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_VLBOX, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_COMBOBOX, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_TOOL_RCLICKED, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_TOOL_ENTER, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_SPINCTRL, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_SPINCTRLDOUBLE, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_TOOL_DROPDOWN, wxCommandEvent );
+wxDEFINE_EVENT( wxEVT_COMBOBOX_DROPDOWN, wxCommandEvent);
+wxDEFINE_EVENT( wxEVT_COMBOBOX_CLOSEUP, wxCommandEvent);
// Mouse event types
wxDEFINE_EVENT( wxEVT_LEFT_DOWN, wxMouseEvent );
// Character input event type
wxDEFINE_EVENT( wxEVT_CHAR, wxKeyEvent );
+wxDEFINE_EVENT( wxEVT_AFTER_CHAR, wxKeyEvent );
wxDEFINE_EVENT( wxEVT_CHAR_HOOK, wxKeyEvent );
wxDEFINE_EVENT( wxEVT_NAVIGATION_KEY, wxNavigationKeyEvent );
wxDEFINE_EVENT( wxEVT_KEY_DOWN, wxKeyEvent );
wxDEFINE_EVENT( wxEVT_UPDATE_UI, wxUpdateUIEvent );
// Clipboard events
-wxDEFINE_EVENT( wxEVT_COMMAND_TEXT_COPY, wxClipboardTextEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_TEXT_CUT, wxClipboardTextEvent );
-wxDEFINE_EVENT( wxEVT_COMMAND_TEXT_PASTE, wxClipboardTextEvent );
+wxDEFINE_EVENT( wxEVT_TEXT_COPY, wxClipboardTextEvent );
+wxDEFINE_EVENT( wxEVT_TEXT_CUT, wxClipboardTextEvent );
+wxDEFINE_EVENT( wxEVT_TEXT_PASTE, wxClipboardTextEvent );
// Generic command events
// Note: a click is a higher-level event than button down/up
wxDEFINE_EVENT( wxEVT_HELP, wxHelpEvent );
wxDEFINE_EVENT( wxEVT_DETAILED_HELP, wxHelpEvent );
-// Thread event
-wxDEFINE_EVENT( wxEVT_COMMAND_THREAD, wxThreadEvent );
-
#endif // wxUSE_GUI
#if wxUSE_BASE
m_handlerToProcessOnlyIn = NULL;
m_isCommandEvent = false;
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
+ m_propagatedFrom = NULL;
m_wasProcessed = false;
+ m_willBeProcessedAgain = false;
}
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)
+ , m_willBeProcessedAgain(false)
{
}
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;
// don't change m_wasProcessed
+ // While the original again could be passed to another handler, this one
+ // isn't going to be processed anywhere else by default.
+ m_willBeProcessedAgain = false;
+
return *this;
}
// wxCommandEvent
// ----------------------------------------------------------------------------
-#ifdef __VISUALC__
- // 'this' : used in base member initializer list (for m_commandString)
- #pragma warning(disable:4355)
-#endif
-
wxCommandEvent::wxCommandEvent(wxEventType commandType, int theId)
: wxEvent(theId, commandType)
{
m_clientData = NULL;
m_clientObject = NULL;
- m_extraLong = 0;
- m_commandInt = 0;
m_isCommandEvent = true;
// the command events are propagated upwards by default
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
}
-#ifdef __VISUALC__
- #pragma warning(default:4355)
-#endif
-
wxString wxCommandEvent::GetString() const
{
- if (m_eventType != wxEVT_COMMAND_TEXT_UPDATED || !m_eventObject)
+ if (m_eventType != wxEVT_TEXT || !m_eventObject)
{
return m_cmdString;
}
((win->GetExtraStyle() & wxWS_EX_PROCESS_UI_UPDATES) == 0)))
return false;
+ // Don't update children of the hidden windows: this is useless as any
+ // change to their state won't be seen by the user anyhow. Notice that this
+ // argument doesn't apply to the hidden windows (with visible parent)
+ // themselves as they could be shown by their EVT_UPDATE_UI handler.
+ if ( win->GetParent() && !win->GetParent()->IsShownOnScreen() )
+ return false;
+
if (sm_updateInterval == -1)
return false;
m_clickCount = -1;
+ m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
m_wheelRotation = 0;
m_wheelDelta = 0;
m_linesPerAction = 0;
- m_wheelAxis = 0;
+ m_columnsPerAction = 0;
}
void wxMouseEvent::Assign(const wxMouseEvent& event)
m_wheelRotation = event.m_wheelRotation;
m_wheelDelta = event.m_wheelDelta;
m_linesPerAction = event.m_linesPerAction;
+ m_columnsPerAction = event.m_columnsPerAction;
m_wheelAxis = event.m_wheelAxis;
}
wxKeyEvent::wxKeyEvent(wxEventType type)
{
m_eventType = type;
- m_keyCode = 0;
+ m_keyCode = WXK_NONE;
#if wxUSE_UNICODE
- m_uniChar = 0;
+ m_uniChar = WXK_NONE;
#endif
+
+ m_x =
+ m_y = wxDefaultCoord;
+ m_hasPosition = false;
+
+ InitPropagation();
}
wxKeyEvent::wxKeyEvent(const wxKeyEvent& evt)
: wxEvent(evt),
wxKeyboardState(evt)
{
- m_x = evt.m_x;
- m_y = evt.m_y;
+ DoAssignMembers(evt);
- m_keyCode = evt.m_keyCode;
- m_rawCode = evt.m_rawCode;
- m_rawFlags = evt.m_rawFlags;
+ InitPropagation();
+}
-#if wxUSE_UNICODE
- m_uniChar = evt.m_uniChar;
-#endif
+wxKeyEvent::wxKeyEvent(wxEventType eventType, const wxKeyEvent& evt)
+ : wxEvent(evt),
+ wxKeyboardState(evt)
+{
+ DoAssignMembers(evt);
+
+ m_eventType = eventType;
+
+ InitPropagation();
+}
+
+void wxKeyEvent::InitPositionIfNecessary() const
+{
+ if ( m_hasPosition )
+ return;
+
+ // We're const because we're called from const Get[XY]() methods but we
+ // need to update the "cached" values.
+ wxKeyEvent& self = const_cast<wxKeyEvent&>(*this);
+ self.m_hasPosition = true;
+
+ // The only position we can possibly associate with the keyboard event on
+ // the platforms where it doesn't carry it already is the mouse position.
+ wxGetMousePosition(&self.m_x, &self.m_y);
+
+ // If this event is associated with a window, the position should be in its
+ // client coordinates, but otherwise leave it in screen coordinates as what
+ // else can we use?
+ wxWindow* const win = wxDynamicCast(GetEventObject(), wxWindow);
+ if ( win )
+ win->ScreenToClient(&self.m_x, &self.m_y);
+}
+
+wxCoord wxKeyEvent::GetX() const
+{
+ InitPositionIfNecessary();
+
+ return m_x;
+}
+
+wxCoord wxKeyEvent::GetY() const
+{
+ InitPositionIfNecessary();
+
+ return m_y;
}
bool wxKeyEvent::IsKeyInCategory(int category) const
delete eTTnode;
}
- delete[] m_eventTypeTable;
- m_eventTypeTable = NULL;
+ wxDELETEA(m_eventTypeTable);
m_size = 0;
}
table = table->baseTable;
}
- // Lets free some memory.
+ // Let's free some memory.
size_t i;
for(i = 0; i < m_size; i++)
{
m_nextHandler == NULL;
}
+wxEventFilter* wxEvtHandler::ms_filterList = NULL;
+
+/* static */ void wxEvtHandler::AddFilter(wxEventFilter* filter)
+{
+ wxCHECK_RET( filter, "NULL filter" );
+
+ filter->m_next = ms_filterList;
+ ms_filterList = filter;
+}
+
+/* static */ void wxEvtHandler::RemoveFilter(wxEventFilter* filter)
+{
+ wxEventFilter* prev = NULL;
+ for ( wxEventFilter* f = ms_filterList; f; f = f->m_next )
+ {
+ if ( f == filter )
+ {
+ // Set the previous list element or the list head to the next
+ // element.
+ if ( prev )
+ prev->m_next = f->m_next;
+ else
+ ms_filterList = f->m_next;
+
+ // Also reset the next pointer in the filter itself just to avoid
+ // having possibly dangling pointers, even though it's not strictly
+ // necessary.
+ f->m_next = NULL;
+
+ // Skip the assert below.
+ return;
+ }
+
+ prev = f;
+ }
+
+ wxFAIL_MSG( "Filter not found" );
+}
+
#if wxUSE_THREADS
bool wxEvtHandler::ProcessThreadEvent(const wxEvent& event)
if ( GetNextHandler() )
return GetNextHandler()->TryAfter(event);
+ // If this event is going to be processed in another handler next, don't
+ // pass it to wxTheApp now, it will be done from TryAfter() of this other
+ // handler.
+ if ( event.WillBeProcessedAgain() )
+ return false;
+
#if WXWIN_COMPATIBILITY_2_8
// as above, call the old virtual function for compatibility
return TryParent(event);
bool wxEvtHandler::ProcessEvent(wxEvent& event)
{
- // The very first thing we do is to allow the application to hook into
- // event processing in order to globally pre-process all events.
+ // The very first thing we do is to allow any registered filters 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
// to avoid calling FilterEvent() multiple times as the event goes through
// the event handler chain and possibly upwards the window hierarchy.
if ( !event.WasProcessed() )
{
- if ( wxTheApp )
+ for ( wxEventFilter* f = ms_filterList; f; f = f->m_next )
{
- int rc = wxTheApp->FilterEvent(event);
- if ( rc != -1 )
+ int rc = f->FilterEvent(event);
+ if ( rc != wxEventFilter::Event_Skip )
{
- wxASSERT_MSG( rc == 1 || rc == 0,
- "unexpected wxApp::FilterEvent return value" );
+ wxASSERT_MSG( rc == wxEventFilter::Event_Ignore ||
+ rc == wxEventFilter::Event_Processed,
+ "unexpected FilterEvent() return value" );
- return rc != 0;
+ return rc != wxEventFilter::Event_Ignore;
}
//else: proceed normally
}
// 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.ShouldProcessOnlyIn(this) )
- return TryHere(event);
+ return TryBeforeAndHere(event);
// Try to process the event in this handler itself.
if ( ProcessEventLocally(event) )
- return true;
+ {
+ // It is possible that DoTryChain() called from ProcessEventLocally()
+ // returned true but the event was not really processed: this happens
+ // if a custom handler ignores the request to process the event in this
+ // handler only and in this case we should skip the post processing
+ // done in TryAfter() but still return the correct value ourselves to
+ // indicate whether we did or did not find a handler for this event.
+ return !event.GetSkipped();
+ }
// If we still didn't find a handler, propagate the event upwards the
// window chain and/or to the application object.
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 ( TryHere(event) )
- return true;
-
- // 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;
+ // Try the hooks which should be called before our own handlers and this
+ // handler itself first. 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()
+ return TryBeforeAndHere(event) || DoTryChain(event);
}
bool wxEvtHandler::DoTryChain(wxEvent& event)
// ProcessEvent() from which we were called or will be done by it when
// we return.
//
- // However we must call ProcessEvent() and not TryHere() because the
+ // However we must call ProcessEvent() and not TryHereOnly() 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.
// done by that rogue event handler.
wxEventProcessInHandlerOnly processInHandlerOnly(event, h);
if ( h->ProcessEvent(event) )
+ {
+ // Make sure "skipped" flag is not set as the event was really
+ // processed in this case. Normally it shouldn't be set anyhow but
+ // make sure just in case the user code does something strange.
+ event.Skip(false);
+
return true;
+ }
if ( !event.ShouldProcessOnlyIn(h) )
- break;
+ {
+ // Still return true to indicate that no further processing should
+ // be undertaken but ensure that "skipped" flag is set so that the
+ // caller knows that the event was not really processed.
+ event.Skip();
+
+ return true;
+ }
}
return false;
}
-bool wxEvtHandler::TryHere(wxEvent& event)
+bool wxEvtHandler::TryHereOnly(wxEvent& event)
{
// If the event handler is disabled it doesn't process any events
if ( !GetEvtHandlerEnabled() )
if ( GetEventHashTable().HandleEvent(event, this) )
return true;
+#ifdef wxHAS_CALL_AFTER
+ // There is an implicit entry for async method calls processing in every
+ // event handler:
+ if ( event.GetEventType() == wxEVT_ASYNC_METHOD_CALL &&
+ event.GetEventObject() == this )
+ {
+ static_cast<wxAsyncMethodCallEvent&>(event).Execute();
+ return true;
+ }
+#endif // wxHAS_CALL_AFTER
+
// We don't have a handler for this event.
return false;
}
if (!m_dynamicEvents)
return false;
- // Remove connection from tracker node (wxEventConnectionRef)
- wxEvtHandler *eventSink = func.GetEvtHandler();
- if ( eventSink && eventSink != this )
- {
- wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
- if ( evtConnRef )
- evtConnRef->DecRef();
- }
-
wxList::compatibility_iterator node = m_dynamicEvents->GetFirst();
while (node)
{
entry->m_fn->IsMatching(func) &&
((entry->m_callbackUserData == userData) || !userData))
{
+ // Remove connection from tracker node (wxEventConnectionRef)
+ wxEvtHandler *eventSink = entry->m_fn->GetEvtHandler();
+ if ( eventSink && eventSink != this )
+ {
+ wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
+ if ( evtConnRef )
+ evtConnRef->DecRef();
+ }
+
delete entry->m_callbackUserData;
m_dynamicEvents->Erase( node );
delete entry;