#include "wx/thread.h"
+#if wxUSE_BASE
+ #include "wx/ptr_scpd.h"
+
+ wxDECLARE_SCOPED_PTR(wxEvent, wxEventPtr)
+ wxDEFINE_SCOPED_PTR(wxEvent, wxEventPtr)
+#endif // wxUSE_BASE
+
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
// global variables
// ----------------------------------------------------------------------------
-// To put pending event handlers
-wxList *wxPendingEvents = (wxList *)NULL;
+// List containing event handlers with pending events (each handler can occur
+// at most once here)
+wxList *wxHandlersWithPendingEvents = NULL;
#if wxUSE_THREADS
- // protects wxPendingEvents list
- wxCriticalSection *wxPendingEventsLocker = (wxCriticalSection *)NULL;
+ // protects wxHandlersWithPendingEvents list
+ wxCriticalSection *wxHandlersWithPendingEventsLocker = NULL;
#endif
-#if !WXWIN_COMPATIBILITY_EVENT_TYPES
-
// common event types are defined here, other event types are defined by the
// components which use them
DEFINE_EVENT_TYPE(wxEVT_SOCKET)
DEFINE_EVENT_TYPE(wxEVT_TIMER)
-#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)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_TOOL_RCLICKED)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_TOOL_ENTER)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_SPINCTRL_UPDATED)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED)
// Mouse event types
DEFINE_EVENT_TYPE(wxEVT_HELP)
DEFINE_EVENT_TYPE(wxEVT_DETAILED_HELP)
-#endif // !WXWIN_COMPATIBILITY_EVENT_TYPES
-
#endif // wxUSE_GUI
#if wxUSE_BASE
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
}
-wxEvent::wxEvent(const wxEvent &src)
+wxEvent::wxEvent(const wxEvent& src)
: wxObject(src)
, m_eventObject(src.m_eventObject)
, m_eventType(src.m_eventType)
{
}
+wxEvent& wxEvent::operator=(const wxEvent& src)
+{
+ wxObject::operator=(src);
+
+ m_eventObject = src.m_eventObject;
+ m_eventType = src.m_eventType;
+ 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;
+
+ return *this;
+}
+
#endif // wxUSE_BASE
#if wxUSE_GUI
wxString wxCommandEvent::GetString() const
{
- if(m_eventType != wxEVT_COMMAND_TEXT_UPDATED || !m_eventObject)
+ if (m_eventType != wxEVT_COMMAND_TEXT_UPDATED || !m_eventObject)
+ {
return m_cmdString;
+ }
else
{
#if wxUSE_TEXTCTRL
wxTextCtrl *txt = wxDynamicCast(m_eventObject, wxTextCtrl);
- if(txt)
+ if ( txt )
return txt->GetValue();
else
#endif // wxUSE_TEXTCTRL
m_aux1Down = false;
m_aux2Down = false;
- m_controlDown = false;
- m_shiftDown = false;
- m_altDown = false;
- m_metaDown = false;
-
m_clickCount = -1;
m_wheelRotation = 0;
void wxMouseEvent::Assign(const wxMouseEvent& event)
{
- m_eventType = event.m_eventType;
+ wxEvent::operator=(event);
+
+ // Borland C++ 5.82 doesn't compile an explicit call to an implicitly
+ // defined operator=() so need to do it this way:
+ *static_cast<wxMouseState *>(this) = event;
m_x = event.m_x;
m_y = event.m_y;
m_aux1Down = event.m_aux1Down;
m_aux2Down = event.m_aux2Down;
- m_controlDown = event.m_controlDown;
- m_shiftDown = event.m_shiftDown;
- m_altDown = event.m_altDown;
- m_metaDown = event.m_metaDown;
-
m_wheelRotation = event.m_wheelRotation;
m_wheelDelta = event.m_wheelDelta;
m_linesPerAction = event.m_linesPerAction;
wxKeyEvent::wxKeyEvent(wxEventType type)
{
m_eventType = type;
- m_shiftDown = false;
- m_controlDown = false;
- m_metaDown = false;
- m_altDown = false;
m_keyCode = 0;
m_scanCode = 0;
#if wxUSE_UNICODE
}
wxKeyEvent::wxKeyEvent(const wxKeyEvent& evt)
- : wxEvent(evt)
+ : wxEvent(evt),
+ wxKeyboardState(evt)
{
m_x = evt.m_x;
m_y = evt.m_y;
m_keyCode = evt.m_keyCode;
- m_controlDown = evt.m_controlDown;
- m_shiftDown = evt.m_shiftDown;
- m_altDown = evt.m_altDown;
- m_metaDown = evt.m_metaDown;
m_scanCode = evt.m_scanCode;
m_rawCode = evt.m_rawCode;
m_rawFlags = evt.m_rawFlags;
#endif
}
-#if WXWIN_COMPATIBILITY_2_6
-long wxKeyEvent::KeyCode() const
-{
- return m_keyCode;
-}
-#endif // WXWIN_COMPATIBILITY_2_6
-
wxWindowCreateEvent::wxWindowCreateEvent(wxWindow *win)
{
SetEventType(wxEVT_CREATE);
delete[] oldEventTypeTable;
}
-
// ----------------------------------------------------------------------------
// wxEvtHandler
// ----------------------------------------------------------------------------
m_enabled = true;
m_dynamicEvents = (wxList *) NULL;
m_pendingEvents = (wxList *) NULL;
-#if wxUSE_THREADS
-# if !defined(__VISAGECPP__)
- m_eventsLocker = new wxCriticalSection;
-# endif
-#endif
// no client data (yet)
m_clientData = NULL;
it != end;
++it )
{
-#if WXWIN_COMPATIBILITY_EVENT_TYPES
- wxEventTableEntry *entry = (wxEventTableEntry*)*it;
-#else // !WXWIN_COMPATIBILITY_EVENT_TYPES
wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)*it;
-#endif // WXWIN_COMPATIBILITY_EVENT_TYPES/!WXWIN_COMPATIBILITY_EVENT_TYPES
+
+ // Remove ourselves from sink destructor notifications
+ // (this has usually been been done, in wxTrackable destructor)
+ wxEvtHandler *eventSink = entry->m_eventSink;
+ if ( eventSink )
+ {
+ wxEventConnectionRef * const
+ evtConnRef = FindRefInTrackerList(eventSink);
+ if ( evtConnRef )
+ {
+ eventSink->RemoveNode(evtConnRef);
+ delete evtConnRef;
+ }
+ }
if (entry->m_callbackUserData)
delete entry->m_callbackUserData;
m_pendingEvents->DeleteContents(true);
delete m_pendingEvents;
-#if wxUSE_THREADS
-# if !defined(__VISAGECPP__)
- delete m_eventsLocker;
-# endif
-
- // Remove us from wxPendingEvents if necessary.
- if(wxPendingEventsLocker)
- wxENTER_CRIT_SECT(*wxPendingEventsLocker);
- if ( wxPendingEvents )
+ // Remove us from wxHandlersWithPendingEvents if necessary.
+ if ( wxHandlersWithPendingEvents )
{
- // Delete all occurences of this from the list of pending events
- while (wxPendingEvents->DeleteObject(this)) { } // Do nothing
- }
- if(wxPendingEventsLocker)
- wxLEAVE_CRIT_SECT(*wxPendingEventsLocker);
+#if wxUSE_THREADS
+ if (wxHandlersWithPendingEventsLocker)
+ wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
#endif
+ if ( wxHandlersWithPendingEvents->DeleteObject(this) )
+ {
+ // check that we were present only once in the list
+ wxASSERT_MSG( !wxHandlersWithPendingEvents->Find(this),
+ "Handler occurs twice in wxHandlersWithPendingEvents list" );
+ }
+ //else: we weren't in this list at all, it's ok
+
+#if wxUSE_THREADS
+ if (wxHandlersWithPendingEventsLocker)
+ wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
+#endif
+ }
+
// we only delete object data, not untyped
if ( m_clientDataType == wxClientData_Object )
delete m_clientObject;
return true;
}
-void wxEvtHandler::ClearEventLocker()
-{
-#if !defined(__VISAGECPP__)
- delete m_eventsLocker;
- m_eventsLocker = NULL;
-#endif
-}
-
#endif // wxUSE_THREADS
-void wxEvtHandler::AddPendingEvent(const wxEvent& event)
+void wxEvtHandler::QueueEvent(wxEvent *event)
{
- // 1) Add event to list of pending events of this event handler
-
- wxEvent *eventCopy = event.Clone();
+ wxCHECK_RET( event, "NULL event can't be posted" );
- // we must be able to copy the events here so the event class must
- // implement Clone() properly instead of just providing a NULL stab for it
- wxCHECK_RET( eventCopy,
- _T("events of this type aren't supposed to be posted") );
-
- wxENTER_CRIT_SECT( Lock() );
+ // 1) Add this event to our list of pending events
+ wxENTER_CRIT_SECT( m_pendingEventsLock );
if ( !m_pendingEvents )
m_pendingEvents = new wxList;
- m_pendingEvents->Append(eventCopy);
-
- wxLEAVE_CRIT_SECT( Lock() );
+ m_pendingEvents->Append(event);
// 2) Add this event handler to list of event handlers that
// have pending events.
- wxENTER_CRIT_SECT(*wxPendingEventsLocker);
+ wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
- if ( !wxPendingEvents )
- wxPendingEvents = new wxList;
- wxPendingEvents->Append(this);
+ if ( !wxHandlersWithPendingEvents )
+ wxHandlersWithPendingEvents = new wxList;
+ if ( !wxHandlersWithPendingEvents->Find(this) )
+ wxHandlersWithPendingEvents->Append(this);
- wxLEAVE_CRIT_SECT(*wxPendingEventsLocker);
+ wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
+
+ // only release m_pendingEventsLock now because otherwise there is a race
+ // condition as described in the ticket #9093: we could process the event
+ // just added to m_pendingEvents in our ProcessPendingEvents() below before
+ // we had time to append this pointer to wxHandlersWithPendingEvents list; thus
+ // breaking the invariant that a handler should be in the list iff it has
+ // any pending events to process
+ wxLEAVE_CRIT_SECT( m_pendingEventsLock );
// 3) Inform the system that new pending events are somewhere,
// and that these should be processed in idle time.
void wxEvtHandler::ProcessPendingEvents()
{
+ wxENTER_CRIT_SECT( m_pendingEventsLock );
+
// this method is only called by wxApp if this handler does have
// pending events
- wxCHECK_RET( m_pendingEvents,
- wxT("Please call wxApp::ProcessPendingEvents() instead") );
-
- wxENTER_CRIT_SECT( Lock() );
-
- // we leave the loop once we have processed all events that were present at
- // the start of ProcessPendingEvents because otherwise we could get into
- // infinite loop if the pending event handler execution resulted in another
- // event being posted
- size_t n = m_pendingEvents->size();
- for ( wxList::compatibility_iterator node = m_pendingEvents->GetFirst();
- node;
- node = m_pendingEvents->GetFirst() )
- {
- wxEvent *event = (wxEvent *)node->GetData();
-
- // It's importan we remove event from list before processing it.
- // Else a nested event loop, for example from a modal dialog, might
- // process the same event again.
-
- m_pendingEvents->Erase(node);
+ wxCHECK_RET( m_pendingEvents && !m_pendingEvents->IsEmpty(),
+ "should have pending events if called" );
- wxLEAVE_CRIT_SECT( Lock() );
+ wxList::compatibility_iterator node = m_pendingEvents->GetFirst();
+ wxEventPtr event(static_cast<wxEvent *>(node->GetData()));
- ProcessEvent(*event);
+ // 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
+ // same event again.
+ m_pendingEvents->Erase(node);
- delete event;
+ // if there are no more pending events left, we don't need to stay in this
+ // list
+ if ( m_pendingEvents->IsEmpty() )
+ {
+#if wxUSE_THREADS
+ if (wxHandlersWithPendingEventsLocker)
+ wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
+#endif
+ wxHandlersWithPendingEvents->DeleteObject(this);
+#if wxUSE_THREADS
+ if (wxHandlersWithPendingEventsLocker)
+ wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker);
+#endif
+ }
- wxENTER_CRIT_SECT( Lock() );
+ wxLEAVE_CRIT_SECT( m_pendingEventsLock );
- if ( --n == 0 )
- break;
- }
+ ProcessEvent(*event);
- wxLEAVE_CRIT_SECT( Lock() );
+ // careful: this object could have been deleted by the event handler
+ // executed by the above ProcessEvent() call, so we can't access any fields
+ // of this object any more
}
/*
bool wxEvtHandler::TryParent(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
//else: proceed normally
}
+ if ( ProcessEventHere(event) )
+ return true;
+
+ // 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() )
{
}
// Try going down the event handler chain
- if ( GetNextHandler() )
- {
- // notice that we shouldn't let the parent have the event even if the
- // next handler does not process it because it will have already passed
- // it to the parent in this case
- return GetNextHandler()->ProcessEvent(event);
- }
+ if ( GetNextHandler() && GetNextHandler()->ProcessEventHere(event) )
+ return true;
- // Finally propagate the event upwards the window chain and/or to the
- // application object as necessary
- return TryParent(event);
+ // We don't have a handler for this event.
+ return false;
}
bool wxEvtHandler::SafelyProcessEvent(wxEvent& event)
}
void wxEvtHandler::Connect( int id, int lastId,
- int eventType,
+ wxEventType eventType,
wxObjectEventFunction func,
wxObject *userData,
wxEvtHandler* eventSink )
{
-#if WXWIN_COMPATIBILITY_EVENT_TYPES
- wxEventTableEntry *entry = new wxEventTableEntry;
- entry->m_eventType = eventType;
- entry->m_id = id;
- entry->m_lastId = lastId;
- entry->m_fn = func;
- entry->m_callbackUserData = userData;
-#else // !WXWIN_COMPATIBILITY_EVENT_TYPES
wxDynamicEventTableEntry *entry =
new wxDynamicEventTableEntry(eventType, id, lastId, func, userData, eventSink);
-#endif // WXWIN_COMPATIBILITY_EVENT_TYPES/!WXWIN_COMPATIBILITY_EVENT_TYPES
if (!m_dynamicEvents)
m_dynamicEvents = new wxList;
// Insert at the front of the list so most recent additions are found first
m_dynamicEvents->Insert( (wxObject*) entry );
+
+ // Make sure we get to know when a sink is destroyed
+ if ( eventSink && eventSink != this )
+ {
+ wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
+ if ( evtConnRef )
+ evtConnRef->IncRef( );
+ else
+ new wxEventConnectionRef(this, eventSink);
+ }
}
bool wxEvtHandler::Disconnect( int id, int lastId, wxEventType eventType,
if (!m_dynamicEvents)
return false;
+ // Remove connection from tracker node (wxEventConnectionRef)
+ if ( eventSink && eventSink != this )
+ {
+ wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
+ if ( evtConnRef )
+ evtConnRef->DecRef();
+ }
+
wxList::compatibility_iterator node = m_dynamicEvents->GetFirst();
while (node)
{
-#if WXWIN_COMPATIBILITY_EVENT_TYPES
- wxEventTableEntry *entry = (wxEventTableEntry*)node->GetData();
-#else // !WXWIN_COMPATIBILITY_EVENT_TYPES
- wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData();
-#endif // WXWIN_COMPATIBILITY_EVENT_TYPES/!WXWIN_COMPATIBILITY_EVENT_TYPES
+ wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData();
if ((entry->m_id == id) &&
((entry->m_lastId == lastId) || (lastId == wxID_ANY)) &&
wxList::compatibility_iterator node = m_dynamicEvents->GetFirst();
while (node)
{
-#if WXWIN_COMPATIBILITY_EVENT_TYPES
- wxEventTableEntry *entry = (wxEventTableEntry*)node->GetData();
-#else // !WXWIN_COMPATIBILITY_EVENT_TYPES
wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData();
-#endif // WXWIN_COMPATIBILITY_EVENT_TYPES/!WXWIN_COMPATIBILITY_EVENT_TYPES
// get next node before (maybe) calling the event handler as it could
// call Disconnect() invalidating the current node
if ((event.GetEventType() == entry->m_eventType) && (entry->m_fn != 0))
{
- wxEvtHandler *handler =
-#if !WXWIN_COMPATIBILITY_EVENT_TYPES
- entry->m_eventSink ? entry->m_eventSink
- :
-#endif
- this;
-
+ wxEvtHandler *handler = entry->m_eventSink ? entry->m_eventSink
+ : this;
if ( ProcessEventIfMatches(*entry, handler, event) )
- {
return true;
- }
}
}
return m_clientData;
}
+// A helper to find an wxEventConnectionRef object
+wxEventConnectionRef *
+wxEvtHandler::FindRefInTrackerList(wxEvtHandler *eventSink)
+{
+ for ( wxTrackerNode *node = eventSink->GetFirst(); node; node = node->m_nxt )
+ {
+ // we only want wxEventConnectionRef nodes here
+ wxEventConnectionRef *evtConnRef = node->ToEventConnection();
+ if ( evtConnRef && evtConnRef->m_src == this )
+ {
+ wxASSERT( evtConnRef->m_sink==eventSink );
+ return evtConnRef;
+ }
+ }
+
+ return NULL;
+}
+
+void wxEvtHandler::OnSinkDestroyed( wxEvtHandler *sink )
+{
+ wxASSERT(m_dynamicEvents);
+
+ // remove all connections with this sink
+ wxList::compatibility_iterator node = m_dynamicEvents->GetFirst(), node_nxt;
+ while (node)
+ {
+ wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData();
+ node_nxt = node->GetNext();
+
+ if ( entry->m_eventSink==sink )
+ {
+ if (entry->m_callbackUserData)
+ delete entry->m_callbackUserData;
+ m_dynamicEvents->Erase( node );
+ delete entry;
+ }
+ node = node_nxt;
+ }
+}
+
#endif // wxUSE_BASE
#if wxUSE_GUI