#ifndef WX_PRECOMP
#include "wx/list.h"
+ #include "wx/log.h"
#include "wx/app.h"
#include "wx/utils.h"
#include "wx/stopwatch.h"
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
// the memory leaks when using it, however this breaks re-initializing the
// library (i.e. repeated calls to wxInitialize/wxUninitialize) because the
// event tables won't be rebuilt the next time, so disable this by default
-#if defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING
+#if wxUSE_MEMORY_TRACING
class wxEventTableEntryModule: public wxModule
{
IMPLEMENT_DYNAMIC_CLASS(wxEventTableEntryModule, wxModule)
-#endif // __WXDEBUG__ && wxUSE_MEMORY_TRACING
+#endif // wxUSE_MEMORY_TRACING
// ----------------------------------------------------------------------------
// global variables
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 event
+wxDEFINE_EVENT( wxEVT_THREAD, wxThreadEvent );
+
#endif // wxUSE_BASE
#if wxUSE_GUI
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);
// 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_HELP, wxHelpEvent );
wxDEFINE_EVENT( wxEVT_DETAILED_HELP, wxHelpEvent );
-// Thread event
-wxDEFINE_EVENT( wxEVT_COMMAND_THREAD, wxThreadEvent );
-
#endif // wxUSE_GUI
#if wxUSE_BASE
m_id = theId;
m_skipped = false;
m_callbackUserData = NULL;
+ m_handlerToProcessOnlyIn = NULL;
m_isCommandEvent = false;
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
m_wasProcessed = false;
, m_timeStamp(src.m_timeStamp)
, m_id(src.m_id)
, m_callbackUserData(src.m_callbackUserData)
+ , m_handlerToProcessOnlyIn(NULL)
, m_propagationLevel(src.m_propagationLevel)
, m_skipped(src.m_skipped)
, m_isCommandEvent(src.m_isCommandEvent)
m_timeStamp = src.m_timeStamp;
m_id = src.m_id;
m_callbackUserData = src.m_callbackUserData;
+ m_handlerToProcessOnlyIn = NULL;
m_propagationLevel = src.m_propagationLevel;
m_skipped = src.m_skipped;
m_isCommandEvent = src.m_isCommandEvent;
#if wxUSE_GUI
-/*
- * Command events
- *
- */
+// ----------------------------------------------------------------------------
+// wxCommandEvent
+// ----------------------------------------------------------------------------
#ifdef __VISUALC__
// 'this' : used in base member initializer list (for m_commandString)
{
m_clientData = NULL;
m_clientObject = NULL;
- m_extraLong = 0;
- m_commandInt = 0;
m_isCommandEvent = true;
// the command events are propagated upwards by default
}
}
-/*
- * UI update events
- */
+// ----------------------------------------------------------------------------
+// wxUpdateUIEvent
+// ----------------------------------------------------------------------------
#if wxUSE_LONGLONG
wxLongLong wxUpdateUIEvent::sm_lastUpdate = 0;
((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;
#endif
}
-/*
- * Scroll events
- */
+// ----------------------------------------------------------------------------
+// wxScrollEvent
+// ----------------------------------------------------------------------------
wxScrollEvent::wxScrollEvent(wxEventType commandType,
int id,
m_commandInt = pos;
}
-/*
- * ScrollWin events
- */
+// ----------------------------------------------------------------------------
+// wxScrollWinEvent
+// ----------------------------------------------------------------------------
wxScrollWinEvent::wxScrollWinEvent(wxEventType commandType,
int pos,
m_commandInt = pos;
}
-/*
- * Mouse events
- *
- */
+// ----------------------------------------------------------------------------
+// wxMouseEvent
+// ----------------------------------------------------------------------------
wxMouseEvent::wxMouseEvent(wxEventType commandType)
{
}
}
-bool wxMouseEvent::ButtonIsDown(int but) const
-{
- switch (but)
- {
- default:
- wxFAIL_MSG(wxT("invalid parameter in wxMouseEvent::ButtonIsDown"));
- // fall through
-
- case wxMOUSE_BTN_ANY:
- return LeftIsDown() || MiddleIsDown() || RightIsDown() || Aux1Down() || Aux2Down();
-
- case wxMOUSE_BTN_LEFT:
- return LeftIsDown();
-
- case wxMOUSE_BTN_MIDDLE:
- return MiddleIsDown();
-
- case wxMOUSE_BTN_RIGHT:
- return RightIsDown();
-
- case wxMOUSE_BTN_AUX1:
- return Aux1IsDown();
-
- case wxMOUSE_BTN_AUX2:
- return Aux2IsDown();
- }
-}
-
int wxMouseEvent::GetButton() const
{
for ( int i = 1; i < wxMOUSE_BTN_MAX; i++ )
return pt;
}
-
-/*
- * Keyboard event
- *
- */
+// ----------------------------------------------------------------------------
+// wxKeyEvent
+// ----------------------------------------------------------------------------
wxKeyEvent::wxKeyEvent(wxEventType type)
{
m_eventType = type;
- m_keyCode = 0;
- m_scanCode = 0;
+ m_keyCode = WXK_NONE;
#if wxUSE_UNICODE
- m_uniChar = 0;
+ m_uniChar = WXK_NONE;
#endif
}
m_y = evt.m_y;
m_keyCode = evt.m_keyCode;
-
- m_scanCode = evt.m_scanCode;
m_rawCode = evt.m_rawCode;
m_rawFlags = evt.m_rawFlags;
#endif
}
+bool wxKeyEvent::IsKeyInCategory(int category) const
+{
+ switch ( GetKeyCode() )
+ {
+ case WXK_LEFT:
+ case WXK_RIGHT:
+ case WXK_UP:
+ case WXK_DOWN:
+ case WXK_NUMPAD_LEFT:
+ case WXK_NUMPAD_RIGHT:
+ case WXK_NUMPAD_UP:
+ case WXK_NUMPAD_DOWN:
+ return (category & WXK_CATEGORY_ARROW) != 0;
+
+ case WXK_PAGEDOWN:
+ case WXK_END:
+ case WXK_NUMPAD_PAGEUP:
+ case WXK_NUMPAD_PAGEDOWN:
+ return (category & WXK_CATEGORY_PAGING) != 0;
+
+ case WXK_HOME:
+ case WXK_PAGEUP:
+ case WXK_NUMPAD_HOME:
+ case WXK_NUMPAD_END:
+ return (category & WXK_CATEGORY_JUMP) != 0;
+
+ case WXK_TAB:
+ case WXK_NUMPAD_TAB:
+ return (category & WXK_CATEGORY_TAB) != 0;
+
+ case WXK_BACK:
+ case WXK_DELETE:
+ case WXK_NUMPAD_DELETE:
+ return (category & WXK_CATEGORY_CUT) != 0;
+
+ default:
+ return false;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// wxWindowCreateEvent
+// ----------------------------------------------------------------------------
+
wxWindowCreateEvent::wxWindowCreateEvent(wxWindow *win)
{
SetEventType(wxEVT_CREATE);
SetEventObject(win);
}
+// ----------------------------------------------------------------------------
+// wxWindowDestroyEvent
+// ----------------------------------------------------------------------------
+
wxWindowDestroyEvent::wxWindowDestroyEvent(wxWindow *win)
{
SetEventType(wxEVT_DESTROY);
SetEventObject(win);
}
+// ----------------------------------------------------------------------------
+// wxChildFocusEvent
+// ----------------------------------------------------------------------------
+
wxChildFocusEvent::wxChildFocusEvent(wxWindow *win)
: wxCommandEvent(wxEVT_CHILD_FOCUS)
{
delete eTTnode;
}
- delete[] m_eventTypeTable;
- m_eventTypeTable = NULL;
+ wxDELETEA(m_eventTypeTable);
m_size = 0;
}
-#if defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING
+#if wxUSE_MEMORY_TRACING
// Clear all tables
void wxEventHashTable::ClearAll()
}
}
-#endif // __WXDEBUG__ && wxUSE_MEMORY_TRACING
+#endif // wxUSE_MEMORY_TRACING
bool wxEventHashTable::HandleEvent(wxEvent &event, wxEvtHandler *self)
{
table = table->baseTable;
}
- // Lets free some memory.
+ // Let's free some memory.
size_t i;
for(i = 0; i < m_size; i++)
{
delete m_dynamicEvents;
}
- if (m_pendingEvents)
- m_pendingEvents->DeleteContents(true);
- delete m_pendingEvents;
-
// Remove us from the list of the pending events if necessary.
- wxEventLoopBase *loop = wxEventLoopBase::GetActive();
- if (loop)
- loop->RemovePendingEventHandler(this);
+ if (wxTheApp)
+ wxTheApp->RemovePendingEventHandler(this);
+
+ DeletePendingEvents();
// we only delete object data, not untyped
if ( m_clientDataType == wxClientData_Object )
{
wxCHECK_RET( event, "NULL event can't be posted" );
- wxEventLoopBase* loop = wxEventLoopBase::GetActive();
- if (!loop)
+ if (!wxTheApp)
{
// we need an event loop which manages the list of event handlers with
// pending events... cannot proceed without it!
- wxLogDebug("No event loop is running!");
+ wxLogDebug("No application object! Cannot queue this event!");
+
+ // anyway delete the given event to avoid memory leaks
+ delete event;
+
return;
}
wxENTER_CRIT_SECT( m_pendingEventsLock );
if ( !m_pendingEvents )
- m_pendingEvents = new wxList;
+ m_pendingEvents = new wxList;
m_pendingEvents->Append(event);
// 2) Add this event handler to list of event handlers that
// have pending events.
- loop->AppendPendingEventHandler(this);
+ wxTheApp->AppendPendingEventHandler(this);
// only release m_pendingEventsLock now because otherwise there is a race
// condition as described in the ticket #9093: we could process the event
wxWakeUpIdle();
}
+void wxEvtHandler::DeletePendingEvents()
+{
+ if (m_pendingEvents)
+ m_pendingEvents->DeleteContents(true);
+ wxDELETE(m_pendingEvents);
+}
+
void wxEvtHandler::ProcessPendingEvents()
{
- wxEventLoopBase* loop = wxEventLoopBase::GetActive();
- if (!loop)
+ if (!wxTheApp)
{
// we need an event loop which manages the list of event handlers with
// pending events... cannot proceed without it!
- wxLogDebug("No event loop is running!");
+ wxLogDebug("No application object! Cannot process pending events!");
return;
}
if (!node)
{
// all our events are NOT processable now... signal this:
- loop->DelayPendingEventHandler(this);
+ wxTheApp->DelayPendingEventHandler(this);
// see the comment at the beginning of evtloop.h header for the
// logic behind YieldFor() and behind DelayPendingEventHandler()
{
// if there are no more pending events left, we don't need to
// stay in this list
- loop->RemovePendingEventHandler(this);
+ wxTheApp->RemovePendingEventHandler(this);
}
wxLEAVE_CRIT_SECT( m_pendingEventsLock );
// of this object any more
}
-/*
- * Event table stuff
- */
-/* static */ bool
-wxEvtHandler::ProcessEventIfMatchesId(const wxEventTableEntryBase& entry,
- wxEvtHandler *handler,
- wxEvent& event)
+/* static */
+bool wxEvtHandler::ProcessEventIfMatchesId(const wxEventTableEntryBase& entry,
+ wxEvtHandler *handler,
+ wxEvent& event)
{
int tableId1 = entry.m_id,
tableId2 = entry.m_lastId;
return false;
}
-bool wxEvtHandler::TryParent(wxEvent& event)
+bool wxEvtHandler::DoTryApp(wxEvent& event)
{
- if ( GetNextHandler() )
- {
- // the next handler will pass it to wxTheApp if it doesn't process it,
- // so return from here to avoid doing it again
- return GetNextHandler()->TryParent(event);
- }
-
if ( wxTheApp && (this != wxTheApp) )
{
// Special case: don't pass wxEVT_IDLE to wxApp, since it'll always
return false;
}
+bool wxEvtHandler::TryBefore(wxEvent& event)
+{
+#if WXWIN_COMPATIBILITY_2_8
+ // call the old virtual function to keep the code overriding it working
+ return TryValidator(event);
+#else
+ wxUnusedVar(event);
+ return false;
+#endif
+}
+
+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);
+#else
+ return DoTryApp(event);
+#endif
+}
+
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 )
{
-/*
- CANNOT ENABLE: ProcessEvent() must always immediately process the event!
-
- if (wxTheApp->IsYielding() &&
- !wxTheApp->IsEventAllowedInsideYield(event.GetEventCategory()))
- {
- wxEvent* queuedEv = event.Clone();
-
- // queue this event rather than processing it now
- QueueEvent(queuedEv);
- // the wxWakeUpIdle call shouldn't probably be done
- // in this context (there's wxYield in the call stack)
-
- return true;
- // it's not completely true that the event was processed;
- // but we cannot even say it was skipped or discarded...
- }
- //else: either we're not inside a wxYield() call or if we are,
- // we can process this event immediately.
-*/
-
int rc = wxTheApp->FilterEvent(event);
if ( rc != -1 )
{
}
}
- if ( ProcessEventHere(event) )
+ // 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 TryBeforeAndHere(event);
+
+
+ // Try to process the event in this handler itself.
+ if ( ProcessEventLocally(event) )
+ {
+ // 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.
+ if ( TryAfter(event) )
return true;
- // pass the event to the next handler, notice that we shouldn't call
- // TryParent() even if it doesn't handle the event as the last handler in
- // the chain will do it
- if ( GetNextHandler() )
- return GetNextHandler()->ProcessEvent(event);
- // propagate the event upwards the window chain and/or to the application
- // object if it wasn't processed at this level
- return TryParent(event);
+ // No handler found anywhere, bail out.
+ return false;
+}
+
+bool wxEvtHandler::ProcessEventLocally(wxEvent& event)
+{
+ // 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)
+{
+ 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 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.
+ //
+ // So we must call ProcessEvent() but it must not do what it usually
+ // does. To resolve this paradox we set up a special flag inside the
+ // object itself to let ProcessEvent() know that it shouldn't do any
+ // pre/post-processing for this event if it gets it. Note that this
+ // only applies to this handler, if the event is passed to another one
+ // by explicitly calling its ProcessEvent(), pre/post-processing should
+ // be done as usual.
+ //
+ // Final complication is that if the implementation of ProcessEvent()
+ // called wxEvent::DidntHonourProcessOnlyIn() (as the gross hack that
+ // is wxScrollHelperEvtHandler::ProcessEvent() does) and ignored our
+ // request to process event in this handler only, we have to compensate
+ // for it by not processing the event further because this was already
+ // 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) )
+ {
+ // 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::ProcessEventHere(wxEvent& event)
+bool wxEvtHandler::TryHereOnly(wxEvent& event)
{
// If the event handler is disabled it doesn't process any events
if ( !GetEvtHandlerEnabled() )
return false;
- // If we have a validator, it has higher priority than our own event
- // handlers
- if ( TryValidator(event) )
- return true;
-
// Handle per-instance dynamic event tables first
if ( m_dynamicEvents && SearchDynamicEventTable(event) )
return true;
}
catch ( ... )
{
- wxEventLoopBase *loop = wxEventLoopBase::GetActive();
+ // notice that we do it in 2 steps to avoid warnings about possibly
+ // uninitialized loop variable from some versions of g++ which are not
+ // smart enough to figure out that GetActive() doesn't throw and so
+ // that loop will always be initialized
+ wxEventLoopBase *loop = NULL;
try
{
+ loop = wxEventLoopBase::GetActive();
+
if ( !wxTheApp || !wxTheApp->OnExceptionInMainLoop() )
{
if ( loop )
#endif // wxUSE_EXCEPTIONS
}
-
bool wxEvtHandler::SearchEventTable(wxEventTable& table, wxEvent& event)
{
const wxEventType eventType = event.GetEventType();
return false;
}
-void wxEvtHandler::DoConnect(int id,
- int lastId,
- wxEventType eventType,
- wxEventFunctor *func,
- wxObject *userData)
+void wxEvtHandler::DoBind(int id,
+ int lastId,
+ wxEventType eventType,
+ wxEventFunctor *func,
+ wxObject *userData)
{
wxDynamicEventTableEntry *entry =
new wxDynamicEventTableEntry(eventType, id, lastId, func, userData);
}
bool
-wxEvtHandler::DoDisconnect(int id,
- int lastId,
- wxEventType eventType,
- const wxEventFunctor& func,
- wxObject *userData)
+wxEvtHandler::DoUnbind(int id,
+ int lastId,
+ wxEventType eventType,
+ const wxEventFunctor& func,
+ wxObject *userData)
{
if (!m_dynamicEvents)
return false;
if ((entry->m_id == id) &&
((entry->m_lastId == lastId) || (lastId == wxID_ANY)) &&
((entry->m_eventType == eventType) || (eventType == wxEVT_NULL)) &&
- entry->m_fn->Matches(func) &&
+ entry->m_fn->IsMatching(func) &&
((entry->m_callbackUserData == userData) || !userData))
{
delete entry->m_callbackUserData;