#include "wx/window.h"
#include "wx/control.h"
#include "wx/dc.h"
+ #include "wx/spinbutt.h"
#include "wx/textctrl.h"
#include "wx/validate.h"
#endif // wxUSE_GUI
#include "wx/thread.h"
#if wxUSE_BASE
- #include "wx/ptr_scpd.h"
+ #include "wx/scopedptr.h"
wxDECLARE_SCOPED_PTR(wxEvent, wxEventPtr)
wxDEFINE_SCOPED_PTR(wxEvent, wxEventPtr)
#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)
// List containing event handlers with pending events (each handler can occur
// at most once here)
wxList *wxHandlersWithPendingEvents = NULL;
+wxList *wxHandlersWithPendingDelayedEvents = NULL;
#if wxUSE_THREADS
// protects wxHandlersWithPendingEvents list
wxDEFINE_EVENT( wxEVT_SCROLL_THUMBRELEASE, wxScrollEvent )
wxDEFINE_EVENT( wxEVT_SCROLL_CHANGED, wxScrollEvent )
+// Due to a bug in older wx versions, wxSpinEvents were being sent with type of
+// wxEVT_SCROLL_LINEUP, wxEVT_SCROLL_LINEDOWN and wxEVT_SCROLL_THUMBTRACK. But
+// with the type-safe events in place, these event types are associated with
+// wxScrollEvent. To allow handling of spin events, new event types have been
+// defined in spinbutt.h/spinnbuttcmn.cpp. To maintain backward compatibility
+// the spin event types are being initialized with the scroll event types.
+
+#if wxUSE_SPINBTN
+
+wxDEFINE_EVENT_ALIAS( wxEVT_SPIN_UP, wxSpinEvent, wxEVT_SCROLL_LINEUP )
+wxDEFINE_EVENT_ALIAS( wxEVT_SPIN_DOWN, wxSpinEvent, wxEVT_SCROLL_LINEDOWN )
+wxDEFINE_EVENT_ALIAS( wxEVT_SPIN, wxSpinEvent, wxEVT_SCROLL_THUMBTRACK )
+
+#endif // wxUSE_SPINBTN
+
// Scroll events from wxWindow
wxDEFINE_EVENT( wxEVT_SCROLLWIN_TOP, wxScrollWinEvent )
wxDEFINE_EVENT( wxEVT_SCROLLWIN_BOTTOM, wxScrollWinEvent )
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
// ----------------------------------------------------------------------------
/*
- * General wxWidgets events, covering
- * all interesting things that might happen (button clicking, resizing,
- * setting text in widgets, etc.).
+ * General wxWidgets events, covering all interesting things that might happen
+ * (button clicking, resizing, setting text in widgets, etc.).
*
* For each completely new event type, derive a new event class.
*
*/
-wxEvent::wxEvent(int theId, wxEventType commandType )
+wxEvent::wxEvent(int theId, wxEventType commandType)
{
m_eventType = commandType;
m_eventObject = NULL;
m_callbackUserData = NULL;
m_isCommandEvent = false;
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
+ m_wasProcessed = false;
}
wxEvent::wxEvent(const wxEvent& src)
, m_propagationLevel(src.m_propagationLevel)
, m_skipped(src.m_skipped)
, m_isCommandEvent(src.m_isCommandEvent)
+ , m_wasProcessed(false)
{
}
m_skipped = src.m_skipped;
m_isCommandEvent = src.m_isCommandEvent;
+ // don't change m_wasProcessed
+
return *this;
}
const size_t count = eventEntryTable.GetCount();
for (size_t n = 0; n < count; n++)
{
- if ( wxEvtHandler::
- ProcessEventIfMatches(*eventEntryTable[n], self, event) )
- {
+ const wxEventTableEntry& entry = *eventEntryTable[n];
+ if ( wxEvtHandler::ProcessEventIfMatchesId(entry, self, event) )
return true;
- }
}
}
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)
{
// Remove ourselves from sink destructor notifications
// (this has usually been done, in wxTrackable destructor)
- wxEvtHandler *eventSink = entry->m_fn->GetHandler();
+ wxEvtHandler *eventSink = entry->m_fn->GetEvtHandler();
if ( eventSink )
{
wxEventConnectionRef * const
}
//else: we weren't in this list at all, it's ok
+ if ( wxHandlersWithPendingDelayedEvents->DeleteObject(this) )
+ {
+ // check that we were present only once in the list
+ wxASSERT_MSG( !wxHandlersWithPendingDelayedEvents->Find(this),
+ "Handler occurs twice in wxHandlersWithPendingDelayedEvents list" );
+ }
+ //else: we weren't in this list at all, it's ok
+
#if wxUSE_THREADS
if (wxHandlersWithPendingEventsLocker)
wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
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)
void wxEvtHandler::ProcessPendingEvents()
{
+ // we need to process only a single pending event in this call because
+ // each call to ProcessEvent() could result in the destruction of this
+ // same event handler (see the comment at the end of this function)
+
wxENTER_CRIT_SECT( m_pendingEventsLock );
// this method is only called by wxApp if this handler does have
"should have pending events if called" );
wxList::compatibility_iterator node = m_pendingEvents->GetFirst();
- wxEventPtr event(static_cast<wxEvent *>(node->GetData()));
+ wxEvent* pEvent = static_cast<wxEvent *>(node->GetData());
+
+ // find the first event which can be processed now:
+ if (wxTheApp && wxTheApp->IsYielding())
+ {
+ while (node && pEvent && !wxTheApp->IsEventAllowedInsideYield(pEvent->GetEventCategory()))
+ {
+ node = node->GetNext();
+ pEvent = node ? static_cast<wxEvent *>(node->GetData()) : NULL;
+ }
+
+ if (!node)
+ {
+ // all our events are NOT processable now... signal this:
+#if wxUSE_THREADS
+ if (wxHandlersWithPendingEventsLocker)
+ wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
+#endif
+ // move us from the list of handlers with processable pending events
+ // to the list of handlers with pending events which needs to be processed later
+ wxHandlersWithPendingEvents->DeleteObject(this);
+ if ( !wxHandlersWithPendingDelayedEvents->Find(this) )
+ wxHandlersWithPendingDelayedEvents->Append(this);
+#if wxUSE_THREADS
+ if (wxHandlersWithPendingEventsLocker)
+ wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
+#endif
+ wxLEAVE_CRIT_SECT( m_pendingEventsLock );
+
+ return;
+ }
+ }
+
+ wxEventPtr event(pEvent);
// it's important we remove event from list before processing it, else a
// nested event loop, for example from a modal dialog, might process the
* Event table stuff
*/
/* static */ bool
-wxEvtHandler::ProcessEventIfMatches(const wxEventTableEntryBase& entry,
- wxEvtHandler *handler,
- wxEvent& event)
+wxEvtHandler::ProcessEventIfMatchesId(const wxEventTableEntryBase& entry,
+ wxEvtHandler *handler,
+ wxEvent& event)
{
int tableId1 = entry.m_id,
tableId2 = entry.m_lastId;
bool wxEvtHandler::ProcessEvent(wxEvent& event)
{
// allow the application to hook into event processing
- if ( wxTheApp )
+ //
+ // 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() )
{
- int rc = wxTheApp->FilterEvent(event);
- if ( rc != -1 )
+ if ( wxTheApp )
{
- wxASSERT_MSG( rc == 1 || rc == 0,
- _T("unexpected wxApp::FilterEvent return value") );
+/*
+ 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 rc != 0;
+ 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 )
+ {
+ wxASSERT_MSG( rc == 1 || rc == 0,
+ "unexpected wxApp::FilterEvent return value" );
+
+ return rc != 0;
+ }
+ //else: proceed normally
}
- //else: proceed normally
}
if ( ProcessEventHere(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);
bool wxEvtHandler::ProcessEventHere(wxEvent& event)
{
- // An event handler can be enabled or disabled
- if ( GetEvtHandlerEnabled() )
- {
- // if we have a validator, it has higher priority than our own event
- // table
- if ( TryValidator(event) )
- return true;
+ // If the event handler is disabled it doesn't process any events
+ if ( !GetEvtHandlerEnabled() )
+ return false;
- // 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
+ // handlers
+ if ( TryValidator(event) )
+ return true;
- // Then static per-class event tables
- if ( GetEventHashTable().HandleEvent(event, this) )
- return true;
- }
+ // Handle per-instance dynamic event tables first
+ if ( m_dynamicEvents && SearchDynamicEventTable(event) )
+ return true;
- // Try going down the event handler chain
- if ( GetNextHandler() && GetNextHandler()->ProcessEventHere(event) )
+ // Then static per-class event tables
+ if ( GetEventHashTable().HandleEvent(event, this) )
return true;
// We don't have a handler for this event.
const wxEventTableEntry& entry = table.entries[i];
if ( eventType == entry.m_eventType )
{
- if ( ProcessEventIfMatches(entry, this, event) )
+ if ( ProcessEventIfMatchesId(entry, this, event) )
return true;
}
}
return false;
}
-void wxEvtHandler::Subscribe( int id, int lastId,
- wxEventType eventType,
- wxEventFunctor *func,
- wxObject *userData )
+void wxEvtHandler::DoConnect(int id,
+ int lastId,
+ wxEventType eventType,
+ wxEventFunctor *func,
+ wxObject *userData)
{
wxDynamicEventTableEntry *entry =
new wxDynamicEventTableEntry(eventType, id, lastId, func, userData);
m_dynamicEvents->Insert( (wxObject*) entry );
// Make sure we get to know when a sink is destroyed
- wxEvtHandler *eventSink = func->GetHandler();
+ wxEvtHandler *eventSink = func->GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
}
bool
-wxEvtHandler::Unsubscribe(int id,
- int lastId,
- wxEventType eventType,
- const wxEventFunctor& func,
- wxObject *userData)
+wxEvtHandler::DoDisconnect(int id,
+ int lastId,
+ wxEventType eventType,
+ const wxEventFunctor& func,
+ wxObject *userData)
{
if (!m_dynamicEvents)
return false;
// Remove connection from tracker node (wxEventConnectionRef)
- wxEvtHandler *eventSink = func.GetHandler();
+ wxEvtHandler *eventSink = func.GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
if ((entry->m_id == id) &&
((entry->m_lastId == lastId) || (lastId == wxID_ANY)) &&
((entry->m_eventType == eventType) || (eventType == wxEVT_NULL)) &&
- (*entry->m_fn == func) &&
+ entry->m_fn->Matches(func) &&
((entry->m_callbackUserData == userData) || !userData))
{
delete entry->m_callbackUserData;
// call Disconnect() invalidating the current node
node = node->GetNext();
- if ((event.GetEventType() == entry->m_eventType) && (entry->m_fn != 0))
+ if ( event.GetEventType() == entry->m_eventType )
{
- wxEvtHandler *handler = entry->m_fn->GetHandler() ? entry->m_fn->GetHandler() : this;
- if ( ProcessEventIfMatches(*entry, handler, event) )
+ wxEvtHandler *handler = entry->m_fn->GetEvtHandler();
+ if ( !handler )
+ handler = this;
+ if ( ProcessEventIfMatchesId(*entry, handler, event) )
return true;
}
}
wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData();
node_nxt = node->GetNext();
- if ( entry->m_fn->GetHandler() == sink )
+ if ( entry->m_fn->GetEvtHandler() == sink )
{
delete entry->m_callbackUserData;
m_dynamicEvents->Erase( node );