diff --git a/docs/changes.txt b/docs/changes.txt index 095511a1d9..4bd6cef11f 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -529,6 +529,7 @@ Major new features in this release All: +- Add wxEvtHandler::CallAfter() method for asynchronous method calls. - Add support for symlinks to wxFileName (David Hart). - Allow testing for symlink/FIFO/socket existence in wxFileName (David Hart). - Many important bug fixes in wxFileSystemWatcher (David Hart). diff --git a/include/wx/event.h b/include/wx/event.h index 5db787ce6c..e28d5c2970 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -33,6 +33,8 @@ #include "wx/meta/convertible.h" #endif +#include "wx/meta/removeref.h" + // ---------------------------------------------------------------------------- // forward declarations // ---------------------------------------------------------------------------- @@ -621,6 +623,7 @@ extern WXDLLIMPEXP_BASE const wxEventType wxEVT_USER_FIRST; // Need events declared to do this class WXDLLIMPEXP_FWD_BASE wxIdleEvent; class WXDLLIMPEXP_FWD_BASE wxThreadEvent; +class WXDLLIMPEXP_FWD_BASE wxAsyncMethodCallEvent; class WXDLLIMPEXP_FWD_CORE wxCommandEvent; class WXDLLIMPEXP_FWD_CORE wxMouseEvent; class WXDLLIMPEXP_FWD_CORE wxFocusEvent; @@ -681,8 +684,9 @@ wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_COMMAND_TOOL_ENTER, wxCommandEv wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_COMMAND_COMBOBOX_DROPDOWN, wxCommandEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_COMMAND_COMBOBOX_CLOSEUP, wxCommandEvent); - // Thread events + // Thread and asynchronous method call events wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_BASE, wxEVT_THREAD, wxThreadEvent); +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_BASE, wxEVT_ASYNC_METHOD_CALL, wxAsyncMethodCallEvent); // Mouse event types wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_LEFT_DOWN, wxMouseEvent); @@ -1266,6 +1270,159 @@ private: }; +// Asynchronous method call events: these event are processed by wxEvtHandler +// itself and result in a call to its Execute() method which simply calls the +// specified method. The difference with a simple method call is that this is +// done asynchronously, i.e. at some later time, instead of immediately when +// the event object is constructed. + +// This is a base class used to process all method calls. +class wxAsyncMethodCallEvent : public wxEvent +{ +public: + wxAsyncMethodCallEvent(wxObject* object) + : wxEvent(wxID_ANY, wxEVT_ASYNC_METHOD_CALL) + { + SetEventObject(object); + } + + wxAsyncMethodCallEvent(const wxAsyncMethodCallEvent& other) + : wxEvent(other) + { + } + + virtual void Execute() = 0; +}; + +// This is a version for calling methods without parameters. +template +class wxAsyncMethodCallEvent0 : public wxAsyncMethodCallEvent +{ +public: + typedef T ObjectType; + typedef void (ObjectType::*MethodType)(); + + wxAsyncMethodCallEvent0(ObjectType* object, + MethodType method) + : wxAsyncMethodCallEvent(object), + m_object(object), + m_method(method) + { + } + + wxAsyncMethodCallEvent0(const wxAsyncMethodCallEvent0& other) + : wxAsyncMethodCallEvent(other), + m_object(other.m_object), + m_method(other.m_method) + { + } + + virtual wxEvent *Clone() const + { + return new wxAsyncMethodCallEvent0(*this); + } + + virtual void Execute() + { + (m_object->*m_method)(); + } + +private: + ObjectType* const m_object; + const MethodType m_method; +}; + +// This is a version for calling methods with a single parameter. +template +class wxAsyncMethodCallEvent1 : public wxAsyncMethodCallEvent +{ +public: + typedef T ObjectType; + typedef void (ObjectType::*MethodType)(T1 x1); + typedef typename wxRemoveRef::type ParamType1; + + wxAsyncMethodCallEvent1(ObjectType* object, + MethodType method, + const ParamType1& x1) + : wxAsyncMethodCallEvent(object), + m_object(object), + m_method(method), + m_param1(x1) + { + } + + wxAsyncMethodCallEvent1(const wxAsyncMethodCallEvent1& other) + : wxAsyncMethodCallEvent(other), + m_object(other.m_object), + m_method(other.m_method), + m_param1(other.m_param1) + { + } + + virtual wxEvent *Clone() const + { + return new wxAsyncMethodCallEvent1(*this); + } + + virtual void Execute() + { + (m_object->*m_method)(m_param1); + } + +private: + ObjectType* const m_object; + const MethodType m_method; + const ParamType1 m_param1; +}; + +// This is a version for calling methods with two parameters. +template +class wxAsyncMethodCallEvent2 : public wxAsyncMethodCallEvent +{ +public: + typedef T ObjectType; + typedef void (ObjectType::*MethodType)(T1 x1, T2 x2); + typedef typename wxRemoveRef::type ParamType1; + typedef typename wxRemoveRef::type ParamType2; + + wxAsyncMethodCallEvent2(ObjectType* object, + MethodType method, + const ParamType1& x1, + const ParamType2& x2) + : wxAsyncMethodCallEvent(object), + m_object(object), + m_method(method), + m_param1(x1), + m_param2(x2) + { + } + + wxAsyncMethodCallEvent2(const wxAsyncMethodCallEvent2& other) + : wxAsyncMethodCallEvent(other), + m_object(other.m_object), + m_method(other.m_method), + m_param1(other.m_param1), + m_param2(other.m_param2) + { + } + + virtual wxEvent *Clone() const + { + return new wxAsyncMethodCallEvent2(*this); + } + + virtual void Execute() + { + (m_object->*m_method)(m_param1, m_param2); + } + +private: + ObjectType* const m_object; + const MethodType m_method; + const ParamType1 m_param1; + const ParamType2 m_param2; +}; + #if wxUSE_GUI @@ -3137,6 +3294,44 @@ public: // NOTE: uses AddPendingEvent(); call only from secondary threads #endif + // Asynchronous method calls: these methods schedule the given method + // pointer for a later call (during the next idle event loop iteration). + // + // Notice that the method is called on this object itself, so the object + // CallAfter() is called on must have the correct dynamic type. + // + // These method can be used from another thread. + + template + void CallAfter(void (T::*method)()) + { + QueueEvent( + new wxAsyncMethodCallEvent0(static_cast(this), method) + ); + } + + // Notice that we use P1 and not T1 for the parameter to allow passing + // parameters that are only convertible to the type taken by the method + // instead of being exactly the same, to be closer to the usual method call + // semantics. + template + void CallAfter(void (T::*method)(T1 x1), P1 x1) + { + QueueEvent( + new wxAsyncMethodCallEvent1( + static_cast(this), method, x1) + ); + } + + template + void CallAfter(void (T::*method)(T1 x1, T2 x2), P1 x1, P2 x2) + { + QueueEvent( + new wxAsyncMethodCallEvent2( + static_cast(this), method, x1, x2) + ); + } + // Connecting and disconnecting // ---------------------------- diff --git a/interface/wx/event.h b/interface/wx/event.h index 3946f05f99..3aae72f4a1 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -477,6 +477,53 @@ public: */ virtual void AddPendingEvent(const wxEvent& event); + /** + Asynchronously call the given method. + + Calling this function on an object schedules an asynchronous call to + the method specified as CallAfter() argument at a (slightly) later + time. This is useful when processing some events as certain actions + typically can't be performed inside their handlers, e.g. you shouldn't + show a modal dialog from a mouse click event handler as this would + break the mouse capture state -- but you can call a method showing + this message dialog after the current event handler completes. + + The method being called must be the method of the object on which + CallAfter() itself is called. + + Notice that it is safe to use CallAfter() from other, non-GUI, + threads, but that the method will be always called in the main, GUI, + thread context. + + Example of use: + @code + class MyFrame : public wxFrame { + void OnClick(wxMouseEvent& event) { + CallAfter(&MyFrame::ShowPosition, event.GetPosition()); + } + + void ShowPosition(const wxPoint& pos) { + if ( wxMessageBox( + wxString::Format("Perform click at (%d, %d)?", + pos.x, pos.y), "", wxYES_NO) == wxYES ) + { + ... do take this click into account ... + } + } + }; + @endcode + + @param method The method to call. + @param x1 The (optional) first parameter to pass to the method. + @param x2 The (optional) second parameter to pass to the method. + + Note that currently only up to 2 arguments can be passed. + + @since 2.9.5 + */ + template + void CallAfter(void (T::*method)(T1, ...), T1 x1, ...); + /** Processes an event, searching event tables and calling zero or more suitable event handler function(s). diff --git a/src/common/event.cpp b/src/common/event.cpp index fa39ae924b..07e5fc3341 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -156,8 +156,9 @@ const wxEventType wxEVT_NULL = wxNewEventType(); wxDEFINE_EVENT( wxEVT_IDLE, wxIdleEvent ); -// Thread event +// Thread and asynchronous call events wxDEFINE_EVENT( wxEVT_THREAD, wxThreadEvent ); +wxDEFINE_EVENT( wxEVT_ASYNC_METHOD_CALL, wxAsyncMethodCallEvent ); #endif // wxUSE_BASE @@ -1564,6 +1565,15 @@ bool wxEvtHandler::TryHereOnly(wxEvent& event) if ( GetEventHashTable().HandleEvent(event, this) ) return true; + // There is an implicit entry for async method calls procession in every + // event handler: + if ( event.GetEventType() == wxEVT_ASYNC_METHOD_CALL && + event.GetEventObject() == this ) + { + static_cast(event).Execute(); + return true; + } + // We don't have a handler for this event. return false; }