From 2ef989c972df0bd023ec3b7252fb91afca55f8ab Mon Sep 17 00:00:00 2001 From: Robert Roebling Date: Tue, 8 Jan 2008 17:57:27 +0000 Subject: [PATCH] Last part from weak ref patch for event sink disconnection git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@51111 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/event.h | 40 +++++++++++++++- include/wx/tracker.h | 2 +- src/common/event.cpp | 109 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 2 deletions(-) diff --git a/include/wx/event.h b/include/wx/event.h index 60d4978b74..7b41f1ff38 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -24,6 +24,7 @@ #include "wx/dynarray.h" #include "wx/thread.h" +#include "wx/tracker.h" // ---------------------------------------------------------------------------- // forward declarations @@ -2255,11 +2256,44 @@ protected: DECLARE_NO_COPY_CLASS(wxEventHashTable) }; +// ---------------------------------------------------------------------------- +// wxEventConnectionRef: A class that represents all connections between two event +// handlers and enables automatic disconnect when an event handler sink goes +// out of scope. Each connection/disconnect increases/decreases ref count, and +// when zero the node goes out of scope. +// ---------------------------------------------------------------------------- + +struct wxEventConnectionRef : public wxTrackerNode { + + wxEventConnectionRef() : m_src(0), m_sink(0), m_refCount(0) { } + wxEventConnectionRef( wxEvtHandler *src, wxEvtHandler *sink ); + virtual ~wxEventConnectionRef(); + + // The sink is being destroyed + virtual void OnObjectDestroy( ); + virtual wxTrackerNodeType GetType( ){ return EventConnectionRef; } + + void IncRef( ) { m_refCount++; } + void DecRef( ); + +protected: + wxEvtHandler *m_src, *m_sink; + int m_refCount; + + friend class wxEvtHandler; + +private: + // It makes no sense to copy objects of this class + wxEventConnectionRef& operator = (const wxEventConnectionRef& WXUNUSED(other)) { wxASSERT(0); return *this; } +}; + + + // ---------------------------------------------------------------------------- // wxEvtHandler: the base class for all objects handling wxWidgets events // ---------------------------------------------------------------------------- -class WXDLLIMPEXP_BASE wxEvtHandler : public wxObject +class WXDLLIMPEXP_BASE wxEvtHandler : public wxObject, public wxTrackableBase { public: wxEvtHandler(); @@ -2359,6 +2393,7 @@ public: // Avoid problems at exit by cleaning up static hash table gracefully void ClearEventHashTable() { GetEventHashTable().Clear(); } + void OnSinkDestroyed( wxEvtHandler *sink ); private: static const wxEventTableEntry sm_eventTableEntries[]; @@ -2425,6 +2460,9 @@ protected: virtual void DoSetClientData( void *data ); virtual void *DoGetClientData() const; + // Search tracker objects for event connection with this sink + wxEventConnectionRef *FindRefInTrackerList( wxEvtHandler *eventSink ); + private: DECLARE_DYNAMIC_CLASS_NO_COPY(wxEvtHandler) }; diff --git a/include/wx/tracker.h b/include/wx/tracker.h index 4f4ec195a7..ef1ab84388 100644 --- a/include/wx/tracker.h +++ b/include/wx/tracker.h @@ -73,7 +73,7 @@ struct wxTrackableBase { wxTrackerNode* GetFirst( ){ return m_first; } // If trying to copy this object, then do not copy its ref list. - wxTrackableBase& operator = (const wxTrackableBase& other) { return *this; } + wxTrackableBase& operator = (const wxTrackableBase& WXUNUSED(other)) { return *this; } protected: wxTrackerNode *m_first; diff --git a/src/common/event.cpp b/src/common/event.cpp index dc121dee42..46efb4f7e5 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -1013,6 +1013,43 @@ void wxEventHashTable::GrowEventTypeTable() } +// ---------------------------------------------------------------------------- +// wxEventConnectionRef +// ---------------------------------------------------------------------------- + +// Below functions are mostly short but kept in cpp file to simplify setting +// breakpoints (GDB) +wxEventConnectionRef::wxEventConnectionRef(wxEvtHandler *src, wxEvtHandler *sink) + : m_src(src), m_sink(sink), m_refCount(1) +{ + wxASSERT( m_sink ); + m_sink->AddNode( this ); +} + +wxEventConnectionRef::~wxEventConnectionRef() +{ +} + +void wxEventConnectionRef::OnObjectDestroy( ) +{ + if( m_src ) + m_src->OnSinkDestroyed( m_sink ); + // It is safe to delete this tracker object here + delete this; +} + +void wxEventConnectionRef::DecRef( ) +{ + if( !--m_refCount ) + { + // The sink holds the only external pointer to this object + if( m_sink ) + m_sink->RemoveNode(this); + delete this; + } +} + + // ---------------------------------------------------------------------------- // wxEvtHandler // ---------------------------------------------------------------------------- @@ -1052,6 +1089,19 @@ wxEvtHandler::~wxEvtHandler() { wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)*it; + // Remove ourselves from sink destructor notifications + // (this has usually been been done, in wxTrackable destructor) + wxEvtHandler *eventSink = entry->m_eventSink; + if( eventSink ) + { + wxEventConnectionRef *pecr = FindRefInTrackerList( eventSink ); + if( pecr ) + { + eventSink->RemoveNode( pecr ); + delete pecr; + } + } + if (entry->m_callbackUserData) delete entry->m_callbackUserData; delete entry; @@ -1347,6 +1397,16 @@ void wxEvtHandler::Connect( int id, int lastId, // 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 ) + { + wxEventConnectionRef *pecr = FindRefInTrackerList( eventSink ); + if( pecr ) + pecr->IncRef( ); + else + pecr = new wxEventConnectionRef(this,eventSink); + } } bool wxEvtHandler::Disconnect( int id, int lastId, wxEventType eventType, @@ -1357,6 +1417,14 @@ bool wxEvtHandler::Disconnect( int id, int lastId, wxEventType eventType, if (!m_dynamicEvents) return false; + // Remove connection from tracker node (wxEventConnectionRef) + if( eventSink ) + { + wxEventConnectionRef *pecr = FindRefInTrackerList( eventSink ); + if( pecr ) + pecr->DecRef( ); + } + wxList::compatibility_iterator node = m_dynamicEvents->GetFirst(); while (node) { @@ -1447,6 +1515,47 @@ void *wxEvtHandler::DoGetClientData() const return m_clientData; } +// A helper func to find an wxEventConnectionRef object +wxEventConnectionRef* wxEvtHandler::FindRefInTrackerList( wxEvtHandler *eventSink ) +{ + wxASSERT(eventSink); + for( wxTrackerNode *ptn=eventSink->GetFirst(); ptn; ptn=ptn->m_nxt ) + { + // Only want wxEventConnectionRef nodes here + if( ptn->GetType()!=wxTrackerNode::EventConnectionRef ) + continue; + wxEventConnectionRef *pecr = static_cast(ptn); + if( pecr && pecr->m_src==this ) + { + wxASSERT( pecr->m_sink==eventSink ); + return pecr; + } + } + 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 -- 2.45.2