]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/event.cpp
xti extensions
[wxWidgets.git] / src / common / event.cpp
index 60ebe589a74ca32f5f5508653088f7e43c90d225..49601ef677cbf98a456328b7ef72ebf410d4d58e 100644 (file)
@@ -44,6 +44,9 @@
 
 #if wxUSE_GUI
     #include "wx/validate.h"
+#if wxUSE_STOPWATCH
+    #include "wx/stopwatch.h"
+#endif
 #endif // wxUSE_GUI
 
 // ----------------------------------------------------------------------------
@@ -101,6 +104,11 @@ const wxEventTable *wxEvtHandler::GetEventTable() const
 const wxEventTable wxEvtHandler::sm_eventTable =
     { (const wxEventTable *)NULL, &wxEvtHandler::sm_eventTableEntries[0] };
 
+wxEventHashTable &wxEvtHandler::GetEventHashTable() const
+    { return wxEvtHandler::sm_eventHashTable; }
+
+wxEventHashTable wxEvtHandler::sm_eventHashTable(wxEvtHandler::sm_eventTable);
+
 const wxEventTableEntry wxEvtHandler::sm_eventTableEntries[] =
     { DECLARE_EVENT_TABLE_ENTRY(wxEVT_NULL, 0, 0, (wxObjectEventFunction)NULL, NULL) };
 
@@ -121,7 +129,21 @@ wxList *wxPendingEvents = (wxList *)NULL;
 // common event types are defined here, other event types are defined by the
 // components which use them
 
+const wxEventType wxEVT_FIRST = 10000;
+const wxEventType wxEVT_USER_FIRST = wxEVT_FIRST + 2000;
+
 DEFINE_EVENT_TYPE(wxEVT_NULL)
+DEFINE_EVENT_TYPE(wxEVT_IDLE)
+DEFINE_EVENT_TYPE(wxEVT_SOCKET)
+
+#endif // !WXWIN_COMPATIBILITY_EVENT_TYPES
+
+#endif // wxUSE_BASE
+
+#if wxUSE_GUI
+
+#if !WXWIN_COMPATIBILITY_EVENT_TYPES
+
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_BUTTON_CLICKED)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_CHECKBOX_CLICKED)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_CHOICE_SELECTED)
@@ -140,7 +162,6 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_TOOL_ENTER)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_SPINCTRL_UPDATED)
 
 // Sockets and timers send events, too
-DEFINE_EVENT_TYPE(wxEVT_SOCKET)
 DEFINE_EVENT_TYPE(wxEVT_TIMER)
 
 // Mouse event types
@@ -181,6 +202,9 @@ DEFINE_EVENT_TYPE(wxEVT_CHAR_HOOK)
 DEFINE_EVENT_TYPE(wxEVT_NAVIGATION_KEY)
 DEFINE_EVENT_TYPE(wxEVT_KEY_DOWN)
 DEFINE_EVENT_TYPE(wxEVT_KEY_UP)
+#if wxUSE_HOTKEY
+DEFINE_EVENT_TYPE(wxEVT_HOTKEY)
+#endif
 
 // Set cursor event
 DEFINE_EVENT_TYPE(wxEVT_SET_CURSOR)
@@ -245,7 +269,6 @@ DEFINE_EVENT_TYPE(wxEVT_DRAW_ITEM)
 DEFINE_EVENT_TYPE(wxEVT_MEASURE_ITEM)
 DEFINE_EVENT_TYPE(wxEVT_COMPARE_ITEM)
 DEFINE_EVENT_TYPE(wxEVT_INIT_DIALOG)
-DEFINE_EVENT_TYPE(wxEVT_IDLE)
 DEFINE_EVENT_TYPE(wxEVT_UPDATE_UI)
 
 // Generic command events
@@ -264,6 +287,10 @@ DEFINE_EVENT_TYPE(wxEVT_DETAILED_HELP)
 
 #endif // !WXWIN_COMPATIBILITY_EVENT_TYPES
 
+#endif // wxUSE_GUI
+
+#if wxUSE_BASE
+
 // ============================================================================
 // implementation
 // ============================================================================
@@ -312,6 +339,7 @@ wxEvent::wxEvent(int theId, wxEventType commandType )
     m_skipped = FALSE;
     m_callbackUserData = (wxObject *) NULL;
     m_isCommandEvent = FALSE;
+    m_propagationLevel = wxEVENT_PROPAGATE_NONE;
 }
 
 wxEvent::wxEvent(const wxEvent &src)
@@ -321,6 +349,7 @@ wxEvent::wxEvent(const wxEvent &src)
     , m_timeStamp(src.m_timeStamp)
     , m_id(src.m_id)
     , m_callbackUserData(src.m_callbackUserData)
+    , m_propagationLevel(src.m_propagationLevel)
     , m_skipped(src.m_skipped)
     , m_isCommandEvent(src.m_isCommandEvent)
 {
@@ -336,14 +365,94 @@ wxEvent::wxEvent(const wxEvent &src)
  */
 
 wxCommandEvent::wxCommandEvent(wxEventType commandType, int theId)
-  : wxEvent(theId, commandType)
+              : wxEvent(theId, commandType)
 {
     m_clientData = (char *) NULL;
     m_clientObject = (wxClientData *) NULL;
     m_extraLong = 0;
     m_commandInt = 0;
-    m_commandString = wxEmptyString;
     m_isCommandEvent = TRUE;
+
+    // the command events are propagated upwards by default
+    m_propagationLevel = wxEVENT_PROPAGATE_MAX;
+}
+
+/*
+ * UI update events
+ */
+
+#if wxUSE_LONGLONG
+wxLongLong wxUpdateUIEvent::sm_lastUpdate = 0;
+#endif
+
+long wxUpdateUIEvent::sm_updateInterval = 0;
+
+wxUpdateUIMode wxUpdateUIEvent::sm_updateMode = wxUPDATE_UI_PROCESS_ALL;
+
+// Can we update?
+bool wxUpdateUIEvent::CanUpdate(wxWindowBase *win)
+{
+    // Don't update if we've switched global updating off
+    // and this window doesn't support updates.
+    if (win &&
+       (GetMode() == wxUPDATE_UI_PROCESS_SPECIFIED &&
+       ((win->GetExtraStyle() & wxWS_EX_PROCESS_UI_UPDATES) == 0)))
+        return FALSE;
+
+    if (sm_updateInterval == -1)
+        return FALSE;
+    else if (sm_updateInterval == 0)
+        return TRUE;
+    else
+    {
+#if wxUSE_STOPWATCH && wxUSE_LONGLONG
+        wxLongLong now = wxGetLocalTimeMillis();
+        if (now > (sm_lastUpdate + sm_updateInterval))
+        {
+            return TRUE;
+        }
+#else
+        // If we don't have wxStopWatch or wxLongLong, we
+        // should err on the safe side and update now anyway.
+        return TRUE;
+#endif
+    }
+    return FALSE;
+}
+
+// Reset the update time to provide a delay until the next
+// time we should update
+void wxUpdateUIEvent::ResetUpdateTime()
+{
+#if wxUSE_STOPWATCH && wxUSE_LONGLONG
+    if (sm_updateInterval > 0)
+    {
+        wxLongLong now = wxGetLocalTimeMillis();
+        if (now > (sm_lastUpdate + sm_updateInterval))
+        {
+            sm_lastUpdate = now;
+        }
+    }
+#endif
+}
+
+/*
+ * Idle events
+ */
+
+wxIdleMode wxIdleEvent::sm_idleMode = wxIDLE_PROCESS_ALL;
+
+// Can we send an idle event?
+bool wxIdleEvent::CanSend(wxWindow* win)
+{
+    // Don't update if we've switched global updating off
+    // and this window doesn't support updates.
+    if (win &&
+       (GetMode() == wxIDLE_PROCESS_SPECIFIED &&
+       ((win->GetExtraStyle() & wxWS_EX_PROCESS_IDLE) == 0)))
+        return FALSE;
+
+    return TRUE;
 }
 
 /*
@@ -604,11 +713,188 @@ wxChildFocusEvent::wxChildFocusEvent(wxWindow *win)
 
 #endif // wxUSE_GUI
 
+
+#if wxUSE_BASE
+
 // ----------------------------------------------------------------------------
-// wxEvtHandler
+// wxEventHashTable
 // ----------------------------------------------------------------------------
 
-#if wxUSE_BASE
+static const int EVENT_TYPE_TABLE_INIT_SIZE = 31; // Not to big not to small...
+
+wxEventHashTable::wxEventHashTable(const wxEventTable &table)
+                : m_table(table),
+                  m_rebuildHash(TRUE)
+{
+    AllocEventTypeTable(EVENT_TYPE_TABLE_INIT_SIZE);
+}
+
+wxEventHashTable::~wxEventHashTable()
+{
+    size_t i;
+    for(i = 0; i < m_size; i++)
+    {
+        EventTypeTablePointer  eTTnode = m_eventTypeTable[i];
+        if (eTTnode)
+        {
+            delete eTTnode;
+        }
+    }
+
+    delete[] m_eventTypeTable;
+}
+
+bool wxEventHashTable::HandleEvent(wxEvent &event, wxEvtHandler *self)
+{
+    if (m_rebuildHash)
+    {
+        InitHashTable();
+        m_rebuildHash = FALSE;
+    }
+
+    // Find all entries for the given event type.
+    wxEventType eventType = event.GetEventType();
+    const EventTypeTablePointer eTTnode = m_eventTypeTable[eventType % m_size];
+    if (eTTnode && eTTnode->eventType == eventType)
+    {
+        // Now start the search for an event handler
+        // that can handle an event with the given ID.
+        int eventId = event.GetId();
+        const wxEventTableEntryPointerArray &eventEntryTable = eTTnode->eventEntryTable;
+
+        size_t n;
+        size_t count = eventEntryTable.GetCount();
+        for (n = 0; n < count; n++)
+        {
+            const wxEventTableEntry* entry = eventEntryTable[n];
+            int tableId1 = entry->m_id,
+                tableId2 = entry->m_lastId;
+
+            if ((tableId1 == -1) ||
+                (tableId2 == -1 && tableId1 == eventId) ||
+                (tableId2 != -1 &&
+                 (eventId >= tableId1 && eventId <= tableId2)))
+            {
+                event.Skip(FALSE);
+                event.m_callbackUserData = entry->m_callbackUserData;
+
+                (self->*((wxEventFunction) (entry->m_fn)))(event);
+
+                if (!event.GetSkipped())
+                    return TRUE;
+            }
+        }
+
+        return FALSE;
+    }
+
+    return FALSE;
+}
+
+void wxEventHashTable::InitHashTable()
+{
+    // Loop over the event tables and all its base tables.
+    const wxEventTable *table = &m_table;
+    while (table)
+    {
+        // Retrieve all valid event handler entries
+        const wxEventTableEntry *entry = table->entries;
+        while (entry->m_fn != 0)
+        {
+            // Add the event entry in the Hash.
+            AddEntry(*entry);
+
+            entry++;
+        }
+
+        table = table->baseTable;
+    }
+
+    // Lets free some memory.
+    size_t i;
+    for(i = 0; i < m_size; i++)
+    {
+        EventTypeTablePointer  eTTnode = m_eventTypeTable[i];
+        if (eTTnode)
+        {
+            eTTnode->eventEntryTable.Shrink();
+        }
+    }
+}
+
+void wxEventHashTable::AddEntry(const wxEventTableEntry &entry)
+{
+    EventTypeTablePointer *peTTnode = &m_eventTypeTable[entry.m_eventType % m_size];
+    EventTypeTablePointer  eTTnode = *peTTnode;
+
+    if (eTTnode)
+    {
+        if (eTTnode->eventType != entry.m_eventType)
+        {
+            // Resize the table!
+            GrowEventTypeTable();
+            // Try again to add it.
+            AddEntry(entry);
+            return;
+        }
+    }
+    else
+    {
+        eTTnode = new EventTypeTable;
+        eTTnode->eventType = entry.m_eventType;
+        *peTTnode = eTTnode;
+    }
+
+    // Fill all hash entries between entry.m_id and entry.m_lastId...
+    eTTnode->eventEntryTable.Add(&entry);
+}
+
+void wxEventHashTable::AllocEventTypeTable(size_t size)
+{
+    m_eventTypeTable = new EventTypeTablePointer[size];
+    memset((void *)m_eventTypeTable, 0, sizeof(EventTypeTablePointer)*size);
+    m_size = size;
+}
+
+void wxEventHashTable::GrowEventTypeTable()
+{
+    size_t oldSize = m_size;
+    EventTypeTablePointer *oldEventTypeTable = m_eventTypeTable;
+
+    // TODO: Search the most optimal grow sequence
+    AllocEventTypeTable(/* GetNextPrime(oldSize) */oldSize*2+1);
+
+    for ( size_t i = 0; i < oldSize; /* */ )
+    {
+        EventTypeTablePointer  eTToldNode = oldEventTypeTable[i];
+        if (eTToldNode)
+        {
+            EventTypeTablePointer *peTTnode = &m_eventTypeTable[eTToldNode->eventType % m_size];
+            EventTypeTablePointer  eTTnode = *peTTnode;
+
+            // Check for collision, we don't want any.
+            if (eTTnode)
+            {
+                GrowEventTypeTable();
+                continue; // Don't increment the counter,
+                          // as we still need to add this element.
+            }
+            else
+            {
+                // Get the old value and put it in the new table.
+                *peTTnode = oldEventTypeTable[i];
+            }
+        }
+
+        i++;
+    }
+
+    delete[] oldEventTypeTable;
+}
+
+// ----------------------------------------------------------------------------
+// wxEvtHandler
+// ----------------------------------------------------------------------------
 
 /*
  * Event handler
@@ -642,19 +928,19 @@ wxEvtHandler::~wxEvtHandler()
 
     if (m_dynamicEvents)
     {
-        wxNode *node = m_dynamicEvents->GetFirst();
-        while (node)
+        wxList::iterator it = m_dynamicEvents->begin(),
+                         en = m_dynamicEvents->end();
+        for (;it != en; ++it)
         {
 #if WXWIN_COMPATIBILITY_EVENT_TYPES
-            wxEventTableEntry *entry = (wxEventTableEntry*)node->GetData();
+            wxEventTableEntry *entry = (wxEventTableEntry*)*it;
 #else // !WXWIN_COMPATIBILITY_EVENT_TYPES
-            wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData();
+            wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)*it;
 #endif // WXWIN_COMPATIBILITY_EVENT_TYPES/!WXWIN_COMPATIBILITY_EVENT_TYPES
 
             if (entry->m_callbackUserData)
                 delete entry->m_callbackUserData;
             delete entry;
-            node = node->GetNext();
         }
         delete m_dynamicEvents;
     };
@@ -665,7 +951,7 @@ wxEvtHandler::~wxEvtHandler()
 #  if !defined(__VISAGECPP__)
     delete m_eventsLocker;
 #  endif
-   
+
     // Remove us from wxPendingEvents if necessary.
     if(wxPendingEventsLocker)
         wxENTER_CRIT_SECT(*wxPendingEventsLocker);
@@ -748,11 +1034,11 @@ void wxEvtHandler::ProcessPendingEvents()
     wxENTER_CRIT_SECT( *m_eventsLocker);
 #endif
 
-    wxNode *node = m_pendingEvents->GetFirst();
+    wxList::compatibility_iterator node = m_pendingEvents->GetFirst();
     while ( node )
     {
         wxEvent *event = (wxEvent *)node->GetData();
-        delete node;
+        m_pendingEvents->Erase(node);
 
         // In ProcessEvent, new events might get added and
         // we can safely leave the crtical section here.
@@ -779,8 +1065,6 @@ void wxEvtHandler::ProcessPendingEvents()
 #endif
 }
 
-#endif // wxUSE_BASE
-
 /*
  * Event table stuff
  */
@@ -821,25 +1105,20 @@ bool wxEvtHandler::ProcessEvent(wxEvent& event)
     // An event handler can be enabled or disabled
     if ( GetEvtHandlerEnabled() )
     {
-        // Handle per-instance dynamic event tables first
-        if ( m_dynamicEvents && SearchDynamicEventTable(event) )
-            return TRUE;
-
+        // if we have a validator, it has higher priority than our own event
+        // table
 #if wxUSE_VALIDATORS
         if ( TryValidator(event) )
             return TRUE;
 #endif // wxUSE_VALIDATORS
 
-        // Then static per-class event tables
-        const wxEventTable *table = GetEventTable();
+        // Handle per-instance dynamic event tables first
+        if ( m_dynamicEvents && SearchDynamicEventTable(event) )
+            return TRUE;
 
-        // Search upwards through the inheritance hierarchy
-        while (table)
-        {
-            if ( SearchEventTable((wxEventTable&)*table, event) )
-                return TRUE;
-            table = table->baseTable;
-        }
+        // Then static per-class event tables
+        if ( GetEventHashTable().HandleEvent(event, this) )
+            return TRUE;
     }
 
     // Try going down the event handler chain
@@ -849,10 +1128,11 @@ bool wxEvtHandler::ProcessEvent(wxEvent& event)
             return TRUE;
     }
 
+    // Finally propagate the event upwards the window chain and/or to the
+    // application object as necessary
     return TryParent(event);
 }
 
-#if wxUSE_BASE
 
 bool wxEvtHandler::SearchEventTable(wxEventTable& table, wxEvent& event)
 {
@@ -901,7 +1181,8 @@ bool wxEvtHandler::SearchEventTable(wxEventTable& table, wxEvent& event)
 void wxEvtHandler::Connect( int id, int lastId,
                             int eventType,
                             wxObjectEventFunction func,
-                            wxObject *userData )
+                            wxObject *userData,
+                            wxEvtHandler* eventSink )
 {
 #if WXWIN_COMPATIBILITY_EVENT_TYPES
     wxEventTableEntry *entry = new wxEventTableEntry;
@@ -912,7 +1193,7 @@ void wxEvtHandler::Connect( int id, int lastId,
     entry->m_callbackUserData = userData;
 #else // !WXWIN_COMPATIBILITY_EVENT_TYPES
     wxDynamicEventTableEntry *entry =
-        new wxDynamicEventTableEntry(eventType, id, lastId, func, userData);
+        new wxDynamicEventTableEntry(eventType, id, lastId, func, userData, eventSink);
 #endif // WXWIN_COMPATIBILITY_EVENT_TYPES/!WXWIN_COMPATIBILITY_EVENT_TYPES
 
     if (!m_dynamicEvents)
@@ -924,12 +1205,13 @@ void wxEvtHandler::Connect( int id, int lastId,
 
 bool wxEvtHandler::Disconnect( int id, int lastId, wxEventType eventType,
                   wxObjectEventFunction func,
-                  wxObject *userData )
+                  wxObject *userData,
+                  wxEvtHandler* eventSink )
 {
     if (!m_dynamicEvents)
         return FALSE;
 
-    wxNode *node = m_dynamicEvents->GetFirst();
+    wxList::compatibility_iterator node = m_dynamicEvents->GetFirst();
     while (node)
     {
 #if WXWIN_COMPATIBILITY_EVENT_TYPES
@@ -942,11 +1224,12 @@ bool wxEvtHandler::Disconnect( int id, int lastId, wxEventType eventType,
             ((entry->m_lastId == lastId) || (lastId == -1)) &&
             ((entry->m_eventType == eventType) || (eventType == wxEVT_NULL)) &&
             ((entry->m_fn == func) || (func == (wxObjectEventFunction)NULL)) &&
+            ((entry->m_eventSink == eventSink) || (eventSink == (wxEvtHandler*)NULL)) &&
             ((entry->m_callbackUserData == userData) || (userData == (wxObject*)NULL)))
         {
             if (entry->m_callbackUserData)
                 delete entry->m_callbackUserData;
-            m_dynamicEvents->DeleteNode( node );
+            m_dynamicEvents->Erase( node );
             delete entry;
             return TRUE;
         }
@@ -962,7 +1245,7 @@ bool wxEvtHandler::SearchDynamicEventTable( wxEvent& event )
 
     int commandId = event.GetId();
 
-    wxNode *node = m_dynamicEvents->GetFirst();
+    wxList::compatibility_iterator node = m_dynamicEvents->GetFirst();
     while (node)
     {
 #if WXWIN_COMPATIBILITY_EVENT_TYPES
@@ -983,7 +1266,12 @@ bool wxEvtHandler::SearchDynamicEventTable( wxEvent& event )
                 event.Skip(FALSE);
                 event.m_callbackUserData = entry->m_callbackUserData;
 
-                (this->*((wxEventFunction) (entry->m_fn)))(event);
+#if !WXWIN_COMPATIBILITY_EVENT_TYPES
+                if (entry->m_eventSink)
+                    ((entry->m_eventSink)->*((wxEventFunction) (entry->m_fn)))(event);
+                else
+#endif
+                    (this->*((wxEventFunction) (entry->m_fn)))(event);
 
                 if ( ! event.GetSkipped() )
                     return TRUE;