]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/event.cpp
Don't handle "Return" key as "TAB" even when the default button is disabled.
[wxWidgets.git] / src / common / event.cpp
index 7118b7dbd9697166ea770f42424b775862e79f35..31e6e6adef4f1cd523ee1ab455906a80c110870d 100644 (file)
     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)
@@ -118,7 +118,7 @@ wxEventHashTable &wxEvtHandler::GetEventHashTable() const
 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
@@ -126,7 +126,7 @@ const wxEventTableEntry wxEvtHandler::sm_eventTableEntries[] =
 // 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
 {
@@ -140,7 +140,7 @@ public:
 
 IMPLEMENT_DYNAMIC_CLASS(wxEventTableEntryModule, wxModule)
 
-#endif // __WXDEBUG__ && wxUSE_MEMORY_TRACING
+#endif // wxUSE_MEMORY_TRACING
 
 // ----------------------------------------------------------------------------
 // global variables
@@ -151,10 +151,13 @@ IMPLEMENT_DYNAMIC_CLASS(wxEventTableEntryModule, wxModule)
 
 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
@@ -177,6 +180,8 @@ 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);
 
 // Mouse event types
 wxDEFINE_EVENT( wxEVT_LEFT_DOWN, wxMouseEvent );
@@ -204,6 +209,7 @@ wxDEFINE_EVENT( wxEVT_AUX2_DCLICK, 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 );
@@ -309,9 +315,6 @@ wxDEFINE_EVENT( wxEVT_COMMAND_ENTER, wxCommandEvent );
 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
@@ -361,6 +364,7 @@ wxEvent::wxEvent(int theId, wxEventType commandType)
     m_id = theId;
     m_skipped = false;
     m_callbackUserData = NULL;
+    m_handlerToProcessOnlyIn = NULL;
     m_isCommandEvent = false;
     m_propagationLevel = wxEVENT_PROPAGATE_NONE;
     m_wasProcessed = false;
@@ -373,6 +377,7 @@ wxEvent::wxEvent(const wxEvent& src)
     , 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)
@@ -389,6 +394,7 @@ wxEvent& wxEvent::operator=(const wxEvent& src)
     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;
@@ -416,8 +422,6 @@ wxCommandEvent::wxCommandEvent(wxEventType commandType, int theId)
 {
     m_clientData = NULL;
     m_clientObject = NULL;
-    m_extraLong = 0;
-    m_commandInt = 0;
     m_isCommandEvent = true;
 
     // the command events are propagated upwards by default
@@ -468,6 +472,13 @@ bool wxUpdateUIEvent::CanUpdate(wxWindowBase *win)
        ((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;
 
@@ -701,34 +712,6 @@ bool wxMouseEvent::Button(int but) const
     }
 }
 
-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++ )
@@ -756,9 +739,9 @@ wxPoint wxMouseEvent::GetLogicalPosition(const wxDC& dc) const
 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
 }
 
@@ -778,6 +761,46 @@ wxKeyEvent::wxKeyEvent(const wxKeyEvent& evt)
 #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
 // ----------------------------------------------------------------------------
@@ -870,13 +893,12 @@ void wxEventHashTable::Clear()
         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()
@@ -889,7 +911,7 @@ void wxEventHashTable::ClearAll()
     }
 }
 
-#endif // __WXDEBUG__ && wxUSE_MEMORY_TRACING
+#endif // wxUSE_MEMORY_TRACING
 
 bool wxEventHashTable::HandleEvent(wxEvent &event, wxEvtHandler *self)
 {
@@ -943,7 +965,7 @@ void wxEventHashTable::InitHashTable()
         table = table->baseTable;
     }
 
-    // Lets free some memory.
+    // Let's free some memory.
     size_t i;
     for(i = 0; i < m_size; i++)
     {
@@ -1079,14 +1101,12 @@ wxEvtHandler::~wxEvtHandler()
         delete m_dynamicEvents;
     }
 
-    if (m_pendingEvents)
-        m_pendingEvents->DeleteContents(true);
-    delete m_pendingEvents;
-
     // Remove us from the list of the pending events if necessary.
     if (wxTheApp)
         wxTheApp->RemovePendingEventHandler(this);
 
+    DeletePendingEvents();
+
     // we only delete object data, not untyped
     if ( m_clientDataType == wxClientData_Object )
         delete m_clientObject;
@@ -1147,7 +1167,7 @@ void wxEvtHandler::QueueEvent(wxEvent *event)
     wxENTER_CRIT_SECT( m_pendingEventsLock );
 
     if ( !m_pendingEvents )
-      m_pendingEvents = new wxList;
+        m_pendingEvents = new wxList;
 
     m_pendingEvents->Append(event);
 
@@ -1169,6 +1189,13 @@ void wxEvtHandler::QueueEvent(wxEvent *event)
     wxWakeUpIdle();
 }
 
+void wxEvtHandler::DeletePendingEvents()
+{
+    if (m_pendingEvents)
+        m_pendingEvents->DeleteContents(true);
+    wxDELETE(m_pendingEvents);
+}
+
 void wxEvtHandler::ProcessPendingEvents()
 {
     if (!wxTheApp)
@@ -1300,7 +1327,7 @@ bool wxEvtHandler::DoTryApp(wxEvent& event)
 
 bool wxEvtHandler::TryBefore(wxEvent& event)
 {
-#ifdef WXWIN_COMPATIBILITY_2_8
+#if WXWIN_COMPATIBILITY_2_8
     // call the old virtual function to keep the code overriding it working
     return TryValidator(event);
 #else
@@ -1311,7 +1338,19 @@ bool wxEvtHandler::TryBefore(wxEvent& event)
 
 bool wxEvtHandler::TryAfter(wxEvent& event)
 {
-#ifdef WXWIN_COMPATIBILITY_2_8
+    // 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
@@ -1321,11 +1360,12 @@ bool wxEvtHandler::TryAfter(wxEvent& 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 )
@@ -1342,30 +1382,102 @@ bool wxEvtHandler::ProcessEvent(wxEvent& event)
         }
     }
 
-    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
-    // 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);
 
-    // propagate the event upwards the window chain and/or to the application
-    // object if it wasn't processed at this level
-    return TryAfter(event);
+    // No handler found anywhere, bail out.
+    return false;
 }
 
-bool wxEvtHandler::ProcessEventHere(wxEvent& event)
+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::TryHereOnly(wxEvent& event)
 {
     // If the event handler is disabled it doesn't process any events
     if ( !GetEvtHandlerEnabled() )
         return false;
 
-    // Try the hooks which should be called before our own handlers
-    if ( TryBefore(event) )
-        return true;
-
     // Handle per-instance dynamic event tables first
     if ( m_dynamicEvents && SearchDynamicEventTable(event) )
         return true;
@@ -1389,9 +1501,15 @@ bool wxEvtHandler::SafelyProcessEvent(wxEvent& event)
     }
     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 )
@@ -1430,11 +1548,11 @@ bool wxEvtHandler::SearchEventTable(wxEventTable& table, wxEvent& event)
     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);
@@ -1458,11 +1576,11 @@ void wxEvtHandler::DoConnect(int id,
 }
 
 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;
@@ -1484,7 +1602,7 @@ wxEvtHandler::DoDisconnect(int id,
         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;