Files
wxWidgets/src/common/event.cpp
Vadim Zeitlin 41268e014d Remove MSVS 2003, a.k.a. MSVC 7, support from the code
It was probably not supported since quite some time anyhow, because wx
own code uses CallAfter() which wouldn't compile with it, but now we can
can get rid of wxHAS_CALL_AFTER checks too.
2021-04-26 15:18:39 +01:00

2067 lines
64 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/event.cpp
// Purpose: Event classes
// Author: Julian Smart
// Modified by:
// Created: 01/02/97
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include "wx/event.h"
#include "wx/eventfilter.h"
#include "wx/evtloop.h"
#ifndef WX_PRECOMP
#include "wx/list.h"
#include "wx/log.h"
#include "wx/app.h"
#include "wx/utils.h"
#include "wx/stopwatch.h"
#include "wx/module.h"
#if wxUSE_GUI
#include "wx/window.h"
#include "wx/control.h"
#include "wx/dc.h"
#include "wx/spinbutt.h"
#include "wx/textentry.h"
#include "wx/validate.h"
#endif // wxUSE_GUI
#endif
#include "wx/thread.h"
#if wxUSE_BASE
#include "wx/scopedptr.h"
wxDECLARE_SCOPED_PTR(wxEvent, wxEventPtr)
wxDEFINE_SCOPED_PTR(wxEvent, wxEventPtr)
#endif // wxUSE_BASE
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
#if wxUSE_BASE
wxIMPLEMENT_DYNAMIC_CLASS(wxEvtHandler, wxObject);
wxIMPLEMENT_ABSTRACT_CLASS(wxEvent, wxObject);
wxIMPLEMENT_DYNAMIC_CLASS(wxIdleEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxThreadEvent, wxEvent);
#endif // wxUSE_BASE
#if wxUSE_GUI
wxIMPLEMENT_DYNAMIC_CLASS(wxCommandEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxNotifyEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxScrollEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxScrollWinEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxMouseEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxKeyEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxSizeEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxPaintEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxNcPaintEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxEraseEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxMoveEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxFocusEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxChildFocusEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxCloseEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxShowEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxMaximizeEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxIconizeEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxFullScreenEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxMenuEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxJoystickEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxDropFilesEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxActivateEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxInitDialogEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxSetCursorEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxSysColourChangedEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxDisplayChangedEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxDPIChangedEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxUpdateUIEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxNavigationKeyEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxPaletteChangedEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxQueryNewPaletteEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxWindowCreateEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxWindowDestroyEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxHelpEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxContextMenuEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxMouseCaptureChangedEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxMouseCaptureLostEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxClipboardTextEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxGestureEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxPanGestureEvent, wxGestureEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxZoomGestureEvent, wxGestureEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxRotateGestureEvent, wxGestureEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxTwoFingerTapEvent, wxGestureEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxLongPressEvent, wxGestureEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxPressAndTapEvent, wxGestureEvent);
#endif // wxUSE_GUI
#if wxUSE_BASE
const wxEventTable *wxEvtHandler::GetEventTable() const
{ return &wxEvtHandler::sm_eventTable; }
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[] =
{ wxDECLARE_EVENT_TABLE_TERMINATOR() };
// wxUSE_MEMORY_TRACING considers memory freed from the static objects dtors
// leaked, so we need to manually clean up all event tables before checking for
// the memory leaks when using it, however this breaks re-initializing the
// library (i.e. repeated calls to wxInitialize/wxUninitialize) because the
// event tables won't be rebuilt the next time, so disable this by default
#if wxUSE_MEMORY_TRACING
class wxEventTableEntryModule: public wxModule
{
public:
wxEventTableEntryModule() { }
virtual bool OnInit() wxOVERRIDE { return true; }
virtual void OnExit() wxOVERRIDE { wxEventHashTable::ClearAll(); }
wxDECLARE_DYNAMIC_CLASS(wxEventTableEntryModule);
};
wxIMPLEMENT_DYNAMIC_CLASS(wxEventTableEntryModule, wxModule);
#endif // wxUSE_MEMORY_TRACING
// ----------------------------------------------------------------------------
// global variables
// ----------------------------------------------------------------------------
// 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;
const wxEventType wxEVT_NULL = wxNewEventType();
wxDEFINE_EVENT( wxEVT_IDLE, wxIdleEvent );
// Thread and asynchronous call events
wxDEFINE_EVENT( wxEVT_THREAD, wxThreadEvent );
wxDEFINE_EVENT( wxEVT_ASYNC_METHOD_CALL, wxAsyncMethodCallEvent );
#endif // wxUSE_BASE
#if wxUSE_GUI
wxDEFINE_EVENT( wxEVT_BUTTON, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_CHECKBOX, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_CHOICE, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_LISTBOX, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_LISTBOX_DCLICK, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_CHECKLISTBOX, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_MENU, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_SLIDER, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_RADIOBOX, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_RADIOBUTTON, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_SCROLLBAR, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_VLBOX, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMBOBOX, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_TOOL_RCLICKED, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_TOOL_ENTER, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_TOOL_DROPDOWN, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMBOBOX_DROPDOWN, wxCommandEvent);
wxDEFINE_EVENT( wxEVT_COMBOBOX_CLOSEUP, wxCommandEvent);
// Mouse event types
wxDEFINE_EVENT( wxEVT_LEFT_DOWN, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_LEFT_UP, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_MIDDLE_DOWN, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_MIDDLE_UP, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_RIGHT_DOWN, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_RIGHT_UP, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_MOTION, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_ENTER_WINDOW, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_LEAVE_WINDOW, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_LEFT_DCLICK, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_MIDDLE_DCLICK, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_RIGHT_DCLICK, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_SET_FOCUS, wxFocusEvent );
wxDEFINE_EVENT( wxEVT_KILL_FOCUS, wxFocusEvent );
wxDEFINE_EVENT( wxEVT_CHILD_FOCUS, wxChildFocusEvent );
wxDEFINE_EVENT( wxEVT_MOUSEWHEEL, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_AUX1_DOWN, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_AUX1_UP, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_AUX1_DCLICK, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_AUX2_DOWN, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_AUX2_UP, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_AUX2_DCLICK, wxMouseEvent );
wxDEFINE_EVENT( wxEVT_MAGNIFY, wxMouseEvent );
// Character input event type
wxDEFINE_EVENT( wxEVT_CHAR, wxKeyEvent );
wxDEFINE_EVENT( wxEVT_AFTER_CHAR, wxKeyEvent );
wxDEFINE_EVENT( wxEVT_CHAR_HOOK, wxKeyEvent );
wxDEFINE_EVENT( wxEVT_NAVIGATION_KEY, wxNavigationKeyEvent );
wxDEFINE_EVENT( wxEVT_KEY_DOWN, wxKeyEvent );
wxDEFINE_EVENT( wxEVT_KEY_UP, wxKeyEvent );
#if wxUSE_HOTKEY
wxDEFINE_EVENT( wxEVT_HOTKEY, wxKeyEvent );
#endif
// Set cursor event
wxDEFINE_EVENT( wxEVT_SET_CURSOR, wxSetCursorEvent );
// wxScrollbar and wxSlider event identifiers
wxDEFINE_EVENT( wxEVT_SCROLL_TOP, wxScrollEvent );
wxDEFINE_EVENT( wxEVT_SCROLL_BOTTOM, wxScrollEvent );
wxDEFINE_EVENT( wxEVT_SCROLL_LINEUP, wxScrollEvent );
wxDEFINE_EVENT( wxEVT_SCROLL_LINEDOWN, wxScrollEvent );
wxDEFINE_EVENT( wxEVT_SCROLL_PAGEUP, wxScrollEvent );
wxDEFINE_EVENT( wxEVT_SCROLL_PAGEDOWN, wxScrollEvent );
wxDEFINE_EVENT( wxEVT_SCROLL_THUMBTRACK, wxScrollEvent );
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_SCROLLWIN_LINEUP, wxScrollWinEvent );
wxDEFINE_EVENT( wxEVT_SCROLLWIN_LINEDOWN, wxScrollWinEvent );
wxDEFINE_EVENT( wxEVT_SCROLLWIN_PAGEUP, wxScrollWinEvent );
wxDEFINE_EVENT( wxEVT_SCROLLWIN_PAGEDOWN, wxScrollWinEvent );
wxDEFINE_EVENT( wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEvent );
wxDEFINE_EVENT( wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEvent );
// Gesture events
wxDEFINE_EVENT( wxEVT_GESTURE_PAN, wxPanGestureEvent );
wxDEFINE_EVENT( wxEVT_GESTURE_ZOOM, wxZoomGestureEvent );
wxDEFINE_EVENT( wxEVT_GESTURE_ROTATE, wxRotateGestureEvent );
wxDEFINE_EVENT( wxEVT_TWO_FINGER_TAP, wxTwoFingerTapEvent );
wxDEFINE_EVENT( wxEVT_LONG_PRESS, wxLongPressEvent );
wxDEFINE_EVENT( wxEVT_PRESS_AND_TAP, wxPressAndTapEvent );
// System events
wxDEFINE_EVENT( wxEVT_SIZE, wxSizeEvent );
wxDEFINE_EVENT( wxEVT_SIZING, wxSizeEvent );
wxDEFINE_EVENT( wxEVT_MOVE, wxMoveEvent );
wxDEFINE_EVENT( wxEVT_MOVING, wxMoveEvent );
wxDEFINE_EVENT( wxEVT_MOVE_START, wxMoveEvent );
wxDEFINE_EVENT( wxEVT_MOVE_END, wxMoveEvent );
wxDEFINE_EVENT( wxEVT_CLOSE_WINDOW, wxCloseEvent );
wxDEFINE_EVENT( wxEVT_END_SESSION, wxCloseEvent );
wxDEFINE_EVENT( wxEVT_QUERY_END_SESSION, wxCloseEvent );
wxDEFINE_EVENT( wxEVT_HIBERNATE, wxActivateEvent );
wxDEFINE_EVENT( wxEVT_ACTIVATE_APP, wxActivateEvent );
wxDEFINE_EVENT( wxEVT_ACTIVATE, wxActivateEvent );
wxDEFINE_EVENT( wxEVT_CREATE, wxWindowCreateEvent );
wxDEFINE_EVENT( wxEVT_DESTROY, wxWindowDestroyEvent );
wxDEFINE_EVENT( wxEVT_SHOW, wxShowEvent );
wxDEFINE_EVENT( wxEVT_ICONIZE, wxIconizeEvent );
wxDEFINE_EVENT( wxEVT_MAXIMIZE, wxMaximizeEvent );
wxDEFINE_EVENT( wxEVT_FULLSCREEN, wxFullScreenEvent );
wxDEFINE_EVENT( wxEVT_MOUSE_CAPTURE_CHANGED, wxMouseCaptureChangedEvent );
wxDEFINE_EVENT( wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEvent );
wxDEFINE_EVENT( wxEVT_PAINT, wxPaintEvent );
wxDEFINE_EVENT( wxEVT_ERASE_BACKGROUND, wxEraseEvent );
wxDEFINE_EVENT( wxEVT_NC_PAINT, wxNcPaintEvent );
wxDEFINE_EVENT( wxEVT_MENU_OPEN, wxMenuEvent );
wxDEFINE_EVENT( wxEVT_MENU_CLOSE, wxMenuEvent );
wxDEFINE_EVENT( wxEVT_MENU_HIGHLIGHT, wxMenuEvent );
wxDEFINE_EVENT( wxEVT_CONTEXT_MENU, wxContextMenuEvent );
wxDEFINE_EVENT( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEvent );
wxDEFINE_EVENT( wxEVT_DISPLAY_CHANGED, wxDisplayChangedEvent );
wxDEFINE_EVENT( wxEVT_DPI_CHANGED, wxDPIChangedEvent );
wxDEFINE_EVENT( wxEVT_QUERY_NEW_PALETTE, wxQueryNewPaletteEvent );
wxDEFINE_EVENT( wxEVT_PALETTE_CHANGED, wxPaletteChangedEvent );
wxDEFINE_EVENT( wxEVT_JOY_BUTTON_DOWN, wxJoystickEvent );
wxDEFINE_EVENT( wxEVT_JOY_BUTTON_UP, wxJoystickEvent );
wxDEFINE_EVENT( wxEVT_JOY_MOVE, wxJoystickEvent );
wxDEFINE_EVENT( wxEVT_JOY_ZMOVE, wxJoystickEvent );
wxDEFINE_EVENT( wxEVT_DROP_FILES, wxDropFilesEvent );
wxDEFINE_EVENT( wxEVT_INIT_DIALOG, wxInitDialogEvent );
wxDEFINE_EVENT( wxEVT_UPDATE_UI, wxUpdateUIEvent );
// Clipboard events
wxDEFINE_EVENT( wxEVT_TEXT_COPY, wxClipboardTextEvent );
wxDEFINE_EVENT( wxEVT_TEXT_CUT, wxClipboardTextEvent );
wxDEFINE_EVENT( wxEVT_TEXT_PASTE, wxClipboardTextEvent );
// Generic command events
// Note: a click is a higher-level event than button down/up
wxDEFINE_EVENT( wxEVT_COMMAND_LEFT_CLICK, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMMAND_LEFT_DCLICK, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMMAND_RIGHT_CLICK, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMMAND_RIGHT_DCLICK, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMMAND_SET_FOCUS, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMMAND_KILL_FOCUS, wxCommandEvent );
wxDEFINE_EVENT( wxEVT_COMMAND_ENTER, wxCommandEvent );
// Help events
wxDEFINE_EVENT( wxEVT_HELP, wxHelpEvent );
wxDEFINE_EVENT( wxEVT_DETAILED_HELP, wxHelpEvent );
#endif // wxUSE_GUI
#if wxUSE_BASE
wxIdleMode wxIdleEvent::sm_idleMode = wxIDLE_PROCESS_ALL;
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// event initialization
// ----------------------------------------------------------------------------
int wxNewEventType()
{
// MT-FIXME
static int s_lastUsedEventType = wxEVT_FIRST;
return s_lastUsedEventType++;
}
// ----------------------------------------------------------------------------
// wxEventFunctor
// ----------------------------------------------------------------------------
wxEventFunctor::~wxEventFunctor()
{
}
// ----------------------------------------------------------------------------
// wxEvent
// ----------------------------------------------------------------------------
/*
* 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)
: m_eventType(commandType)
{
m_eventObject = NULL;
m_timeStamp = 0;
m_id = theId;
m_skipped = false;
m_callbackUserData = NULL;
m_handlerToProcessOnlyIn = NULL;
m_isCommandEvent = false;
m_propagationLevel = wxEVENT_PROPAGATE_NONE;
m_propagatedFrom = NULL;
m_wasProcessed = false;
m_willBeProcessedAgain = false;
}
wxEvent::wxEvent(const wxEvent& src)
: wxObject(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_handlerToProcessOnlyIn(NULL)
, m_propagationLevel(src.m_propagationLevel)
, m_propagatedFrom(NULL)
, m_skipped(src.m_skipped)
, m_isCommandEvent(src.m_isCommandEvent)
, m_wasProcessed(false)
, m_willBeProcessedAgain(false)
{
}
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_handlerToProcessOnlyIn = NULL;
m_propagationLevel = src.m_propagationLevel;
m_propagatedFrom = NULL;
m_skipped = src.m_skipped;
m_isCommandEvent = src.m_isCommandEvent;
// don't change m_wasProcessed
// While the original again could be passed to another handler, this one
// isn't going to be processed anywhere else by default.
m_willBeProcessedAgain = false;
return *this;
}
#endif // wxUSE_BASE
#if wxUSE_GUI
// ----------------------------------------------------------------------------
// wxCommandEvent
// ----------------------------------------------------------------------------
wxString wxCommandEvent::GetString() const
{
// This is part of the hack retrieving the event string from the control
// itself only when/if it's really needed to avoid copying potentially huge
// strings coming from multiline text controls.
if (m_eventType == wxEVT_TEXT && m_eventObject)
{
// Only windows generate wxEVT_TEXT events, so this cast should really
// succeed, but err on the side of caution just in case somebody
// created a bogus event of this type.
if ( wxWindow* const w = wxDynamicCast(m_eventObject, wxWindow) )
{
if ( const wxTextEntry* const entry = w->WXGetTextEntry() )
return entry->GetValue();
}
}
return m_cmdString;
}
// ----------------------------------------------------------------------------
// wxPaintEvent and wxNcPaintEvent
// ----------------------------------------------------------------------------
wxPaintEvent::wxPaintEvent(wxWindowBase* window)
: wxEvent(window ? window->GetId() : 0, wxEVT_PAINT)
{
SetEventObject(window);
}
wxNcPaintEvent::wxNcPaintEvent(wxWindowBase* window)
: wxEvent(window ? window->GetId() : 0, wxEVT_NC_PAINT)
{
SetEventObject(window);
}
// ----------------------------------------------------------------------------
// wxUpdateUIEvent
// ----------------------------------------------------------------------------
#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;
// Don't update children of the hidden windows: this is useless as any
// change to their state won't be seen by the user anyhow. Notice that this
// argument doesn't apply to the hidden windows (with visible parent)
// themselves as they could be shown by their EVT_UPDATE_UI handler.
if ( win->GetParent() && !win->GetParent()->IsShownOnScreen() )
return false;
if (sm_updateInterval == -1)
return false;
if (sm_updateInterval == 0)
return true;
#if wxUSE_STOPWATCH && wxUSE_LONGLONG
wxLongLong now = wxGetLocalTimeMillis();
if (now > (sm_lastUpdate + sm_updateInterval))
{
return true;
}
return false;
#else
// If we don't have wxStopWatch or wxLongLong, we
// should err on the safe side and update now anyway.
return true;
#endif
}
// 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
}
// ----------------------------------------------------------------------------
// wxScrollEvent
// ----------------------------------------------------------------------------
wxScrollEvent::wxScrollEvent(wxEventType commandType,
int id,
int pos,
int orient)
: wxCommandEvent(commandType, id)
{
m_extraLong = orient;
m_commandInt = pos;
}
// ----------------------------------------------------------------------------
// wxScrollWinEvent
// ----------------------------------------------------------------------------
wxScrollWinEvent::wxScrollWinEvent(wxEventType commandType,
int pos,
int orient)
{
m_eventType = commandType;
m_extraLong = orient;
m_commandInt = pos;
}
// ----------------------------------------------------------------------------
// wxMouseEvent
// ----------------------------------------------------------------------------
wxMouseEvent::wxMouseEvent(wxEventType commandType)
{
m_eventType = commandType;
m_x = 0;
m_y = 0;
m_leftDown = false;
m_middleDown = false;
m_rightDown = false;
m_aux1Down = false;
m_aux2Down = false;
m_clickCount = -1;
m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
m_wheelRotation = 0;
m_wheelDelta = 0;
m_wheelInverted = false;
m_linesPerAction = 0;
m_columnsPerAction = 0;
m_magnification = 0.0f;
}
void wxMouseEvent::Assign(const wxMouseEvent& event)
{
wxEvent::operator=(event);
wxMouseState::operator=(event);
m_x = event.m_x;
m_y = event.m_y;
m_leftDown = event.m_leftDown;
m_middleDown = event.m_middleDown;
m_rightDown = event.m_rightDown;
m_aux1Down = event.m_aux1Down;
m_aux2Down = event.m_aux2Down;
m_wheelRotation = event.m_wheelRotation;
m_wheelDelta = event.m_wheelDelta;
m_wheelInverted = event.m_wheelInverted;
m_linesPerAction = event.m_linesPerAction;
m_columnsPerAction = event.m_columnsPerAction;
m_wheelAxis = event.m_wheelAxis;
m_magnification = event.m_magnification;
}
// return true if was a button dclick event
bool wxMouseEvent::ButtonDClick(int but) const
{
switch (but)
{
default:
wxFAIL_MSG(wxT("invalid parameter in wxMouseEvent::ButtonDClick"));
wxFALLTHROUGH;
case wxMOUSE_BTN_ANY:
return (LeftDClick() || MiddleDClick() || RightDClick() ||
Aux1DClick() || Aux2DClick());
case wxMOUSE_BTN_LEFT:
return LeftDClick();
case wxMOUSE_BTN_MIDDLE:
return MiddleDClick();
case wxMOUSE_BTN_RIGHT:
return RightDClick();
case wxMOUSE_BTN_AUX1:
return Aux1DClick();
case wxMOUSE_BTN_AUX2:
return Aux2DClick();
}
}
// return true if was a button down event
bool wxMouseEvent::ButtonDown(int but) const
{
switch (but)
{
default:
wxFAIL_MSG(wxT("invalid parameter in wxMouseEvent::ButtonDown"));
wxFALLTHROUGH;
case wxMOUSE_BTN_ANY:
return (LeftDown() || MiddleDown() || RightDown() ||
Aux1Down() || Aux2Down());
case wxMOUSE_BTN_LEFT:
return LeftDown();
case wxMOUSE_BTN_MIDDLE:
return MiddleDown();
case wxMOUSE_BTN_RIGHT:
return RightDown();
case wxMOUSE_BTN_AUX1:
return Aux1Down();
case wxMOUSE_BTN_AUX2:
return Aux2Down();
}
}
// return true if was a button up event
bool wxMouseEvent::ButtonUp(int but) const
{
switch (but)
{
default:
wxFAIL_MSG(wxT("invalid parameter in wxMouseEvent::ButtonUp"));
wxFALLTHROUGH;
case wxMOUSE_BTN_ANY:
return (LeftUp() || MiddleUp() || RightUp() ||
Aux1Up() || Aux2Up());
case wxMOUSE_BTN_LEFT:
return LeftUp();
case wxMOUSE_BTN_MIDDLE:
return MiddleUp();
case wxMOUSE_BTN_RIGHT:
return RightUp();
case wxMOUSE_BTN_AUX1:
return Aux1Up();
case wxMOUSE_BTN_AUX2:
return Aux2Up();
}
}
// return true if the given button is currently changing state
bool wxMouseEvent::Button(int but) const
{
switch (but)
{
default:
wxFAIL_MSG(wxT("invalid parameter in wxMouseEvent::Button"));
wxFALLTHROUGH;
case wxMOUSE_BTN_ANY:
return ButtonUp(wxMOUSE_BTN_ANY) ||
ButtonDown(wxMOUSE_BTN_ANY) ||
ButtonDClick(wxMOUSE_BTN_ANY);
case wxMOUSE_BTN_LEFT:
return LeftDown() || LeftUp() || LeftDClick();
case wxMOUSE_BTN_MIDDLE:
return MiddleDown() || MiddleUp() || MiddleDClick();
case wxMOUSE_BTN_RIGHT:
return RightDown() || RightUp() || RightDClick();
case wxMOUSE_BTN_AUX1:
return Aux1Down() || Aux1Up() || Aux1DClick();
case wxMOUSE_BTN_AUX2:
return Aux2Down() || Aux2Up() || Aux2DClick();
}
}
int wxMouseEvent::GetButton() const
{
for ( int i = 1; i < wxMOUSE_BTN_MAX; i++ )
{
if ( Button(i) )
{
return i;
}
}
return wxMOUSE_BTN_NONE;
}
// Find the logical position of the event given the DC
wxPoint wxMouseEvent::GetLogicalPosition(const wxDC& dc) const
{
wxPoint pt(dc.DeviceToLogicalX(m_x), dc.DeviceToLogicalY(m_y));
return pt;
}
// ----------------------------------------------------------------------------
// wxKeyEvent
// ----------------------------------------------------------------------------
wxKeyEvent::wxKeyEvent(wxEventType type)
#if wxUSE_UNICODE
: m_uniChar(WXK_NONE)
#endif
{
m_eventType = type;
m_keyCode = WXK_NONE;
m_x =
m_y = wxDefaultCoord;
m_hasPosition = false;
InitPropagation();
}
wxKeyEvent::wxKeyEvent(const wxKeyEvent& evt)
: wxEvent(evt),
wxKeyboardState(evt)
{
DoAssignMembers(evt);
InitPropagation();
}
wxKeyEvent::wxKeyEvent(wxEventType eventType, const wxKeyEvent& evt)
: wxEvent(evt),
wxKeyboardState(evt)
{
DoAssignMembers(evt);
m_eventType = eventType;
InitPropagation();
}
wxKeyEvent& wxKeyEvent::operator=(const wxKeyEvent& evt)
{
if ( &evt != this )
{
wxEvent::operator=(evt);
wxKeyboardState::operator=(evt);
DoAssignMembers(evt);
}
return *this;
}
void wxKeyEvent::InitPositionIfNecessary() const
{
if ( m_hasPosition )
return;
// We're const because we're called from const Get[XY]() methods but we
// need to update the "cached" values.
wxKeyEvent& self = const_cast<wxKeyEvent&>(*this);
self.m_hasPosition = true;
// The only position we can possibly associate with the keyboard event on
// the platforms where it doesn't carry it already is the mouse position.
wxGetMousePosition(&self.m_x, &self.m_y);
// If this event is associated with a window, the position should be in its
// client coordinates, but otherwise leave it in screen coordinates as what
// else can we use?
wxWindow* const win = wxDynamicCast(GetEventObject(), wxWindow);
if ( win )
win->ScreenToClient(&self.m_x, &self.m_y);
}
wxCoord wxKeyEvent::GetX() const
{
InitPositionIfNecessary();
return m_x;
}
wxCoord wxKeyEvent::GetY() const
{
InitPositionIfNecessary();
return m_y;
}
bool wxKeyEvent::IsKeyInCategory(int category) const
{
switch ( GetKeyCode() )
{
case WXK_LEFT:
case WXK_RIGHT:
case WXK_UP:
case WXK_DOWN:
case WXK_NUMPAD_LEFT:
case WXK_NUMPAD_RIGHT:
case WXK_NUMPAD_UP:
case WXK_NUMPAD_DOWN:
return (category & WXK_CATEGORY_ARROW) != 0;
case WXK_PAGEDOWN:
case WXK_PAGEUP:
case WXK_NUMPAD_PAGEUP:
case WXK_NUMPAD_PAGEDOWN:
return (category & WXK_CATEGORY_PAGING) != 0;
case WXK_HOME:
case WXK_END:
case WXK_NUMPAD_HOME:
case WXK_NUMPAD_END:
return (category & WXK_CATEGORY_JUMP) != 0;
case WXK_TAB:
case WXK_NUMPAD_TAB:
return (category & WXK_CATEGORY_TAB) != 0;
case WXK_BACK:
case WXK_DELETE:
case WXK_NUMPAD_DELETE:
return (category & WXK_CATEGORY_CUT) != 0;
default:
return false;
}
}
// ----------------------------------------------------------------------------
// wxWindowCreateEvent
// ----------------------------------------------------------------------------
wxWindowCreateEvent::wxWindowCreateEvent(wxWindow *win)
{
SetEventType(wxEVT_CREATE);
SetEventObject(win);
}
// ----------------------------------------------------------------------------
// wxWindowDestroyEvent
// ----------------------------------------------------------------------------
wxWindowDestroyEvent::wxWindowDestroyEvent(wxWindow *win)
{
SetEventType(wxEVT_DESTROY);
SetEventObject(win);
}
// ----------------------------------------------------------------------------
// wxChildFocusEvent
// ----------------------------------------------------------------------------
wxChildFocusEvent::wxChildFocusEvent(wxWindow *win)
: wxCommandEvent(wxEVT_CHILD_FOCUS)
{
SetEventObject(win);
}
// ----------------------------------------------------------------------------
// wxHelpEvent
// ----------------------------------------------------------------------------
/* static */
wxHelpEvent::Origin wxHelpEvent::GuessOrigin(Origin origin)
{
if ( origin == Origin_Unknown )
{
// assume that the event comes from the help button if it's not from
// keyboard and that pressing F1 always results in the help event
origin = wxGetKeyState(WXK_F1) ? Origin_Keyboard : Origin_HelpButton;
}
return origin;
}
#endif // wxUSE_GUI
#if wxUSE_BASE
// ----------------------------------------------------------------------------
// wxEventHashTable
// ----------------------------------------------------------------------------
static const int EVENT_TYPE_TABLE_INIT_SIZE = 31; // Not too big not too small...
wxEventHashTable* wxEventHashTable::sm_first = NULL;
wxEventHashTable::wxEventHashTable(const wxEventTable &table)
: m_table(table),
m_rebuildHash(true)
{
AllocEventTypeTable(EVENT_TYPE_TABLE_INIT_SIZE);
m_next = sm_first;
if (m_next)
m_next->m_previous = this;
sm_first = this;
}
wxEventHashTable::~wxEventHashTable()
{
if (m_next)
m_next->m_previous = m_previous;
if (m_previous)
m_previous->m_next = m_next;
if (sm_first == this)
sm_first = m_next;
Clear();
}
void wxEventHashTable::Clear()
{
for ( size_t i = 0; i < m_size; i++ )
{
EventTypeTablePointer eTTnode = m_eventTypeTable[i];
delete eTTnode;
}
wxDELETEA(m_eventTypeTable);
m_size = 0;
}
#if wxUSE_MEMORY_TRACING
// Clear all tables
void wxEventHashTable::ClearAll()
{
wxEventHashTable* table = sm_first;
while (table)
{
table->Clear();
table = table->m_next;
}
}
#endif // wxUSE_MEMORY_TRACING
bool wxEventHashTable::HandleEvent(wxEvent &event, wxEvtHandler *self)
{
if (m_rebuildHash)
{
InitHashTable();
m_rebuildHash = false;
}
if (!m_eventTypeTable)
return 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.
const wxEventTableEntryPointerArray&
eventEntryTable = eTTnode->eventEntryTable;
const size_t count = eventEntryTable.GetCount();
for (size_t n = 0; n < count; n++)
{
const wxEventTableEntry& entry = *eventEntryTable[n];
if ( wxEvtHandler::ProcessEventIfMatchesId(entry, self, event) )
return true;
}
}
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;
}
// Let's 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)
{
// This might happen 'accidentally' as the app is exiting
if (!m_eventTypeTable)
return;
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
// ----------------------------------------------------------------------------
wxEvtHandler::wxEvtHandler()
{
m_nextHandler = NULL;
m_previousHandler = NULL;
m_enabled = true;
m_dynamicEvents = NULL;
m_pendingEvents = NULL;
// no client data (yet)
m_clientData = NULL;
m_clientDataType = wxClientData_None;
}
wxEvtHandler::~wxEvtHandler()
{
Unlink();
if (m_dynamicEvents)
{
size_t cookie;
for ( wxDynamicEventTableEntry* entry = GetFirstDynamicEntry(cookie);
entry;
entry = GetNextDynamicEntry(cookie) )
{
// Remove ourselves from sink destructor notifications
// (this has usually been done, in wxTrackable destructor)
wxEvtHandler *eventSink = entry->m_fn->GetEvtHandler();
if ( eventSink )
{
wxEventConnectionRef * const
evtConnRef = FindRefInTrackerList(eventSink);
if ( evtConnRef )
{
eventSink->RemoveNode(evtConnRef);
delete evtConnRef;
}
}
delete entry->m_callbackUserData;
delete entry;
}
delete m_dynamicEvents;
}
// Remove us from the list of the pending events if necessary.
if (wxTheApp)
wxTheApp->RemovePendingEventHandler(this);
DeletePendingEvents();
// we only delete object data, not untyped
if ( m_clientDataType == wxClientData_Object )
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;
}
wxEventFilter* wxEvtHandler::ms_filterList = NULL;
/* static */ void wxEvtHandler::AddFilter(wxEventFilter* filter)
{
wxCHECK_RET( filter, "NULL filter" );
filter->m_next = ms_filterList;
ms_filterList = filter;
}
/* static */ void wxEvtHandler::RemoveFilter(wxEventFilter* filter)
{
wxEventFilter* prev = NULL;
for ( wxEventFilter* f = ms_filterList; f; f = f->m_next )
{
if ( f == filter )
{
// Set the previous list element or the list head to the next
// element.
if ( prev )
prev->m_next = f->m_next;
else
ms_filterList = f->m_next;
// Also reset the next pointer in the filter itself just to avoid
// having possibly dangling pointers, even though it's not strictly
// necessary.
f->m_next = NULL;
// Skip the assert below.
return;
}
prev = f;
}
wxFAIL_MSG( "Filter not found" );
}
#if wxUSE_THREADS
bool wxEvtHandler::ProcessThreadEvent(const wxEvent& event)
{
// check that we are really in a child thread
wxASSERT_MSG( !wxThread::IsMain(),
wxT("use ProcessEvent() in main thread") );
AddPendingEvent(event);
return true;
}
#endif // wxUSE_THREADS
void wxEvtHandler::QueueEvent(wxEvent *event)
{
wxCHECK_RET( event, "NULL event can't be posted" );
if (!wxTheApp)
{
// we need an event loop which manages the list of event handlers with
// pending events... cannot proceed without it!
wxLogDebug("No application object! Cannot queue this event!");
// anyway delete the given event to avoid memory leaks
delete event;
return;
}
// 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(event);
// 2) Add this event handler to list of event handlers that
// have pending events.
wxTheApp->AppendPendingEventHandler(this);
// 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.
wxWakeUpIdle();
}
void wxEvtHandler::DeletePendingEvents()
{
if (m_pendingEvents)
m_pendingEvents->DeleteContents(true);
wxDELETE(m_pendingEvents);
}
void wxEvtHandler::ProcessPendingEvents()
{
if (!wxTheApp)
{
// we need an event loop which manages the list of event handlers with
// pending events... cannot proceed without it!
wxLogDebug("No application object! Cannot process pending events!");
return;
}
// 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
// pending events
wxCHECK_RET( m_pendingEvents && !m_pendingEvents->IsEmpty(),
"should have pending events if called" );
wxList::compatibility_iterator node = m_pendingEvents->GetFirst();
wxEvent* pEvent = static_cast<wxEvent *>(node->GetData());
// find the first event which can be processed now:
wxEventLoopBase* evtLoop = wxEventLoopBase::GetActive();
if (evtLoop && evtLoop->IsYielding())
{
while (node && pEvent && !evtLoop->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:
wxTheApp->DelayPendingEventHandler(this);
// see the comment at the beginning of evtloop.h header for the
// logic behind YieldFor() and behind DelayPendingEventHandler()
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
// same event again.
m_pendingEvents->Erase(node);
if ( m_pendingEvents->IsEmpty() )
{
// if there are no more pending events left, we don't need to
// stay in this list
wxTheApp->RemovePendingEventHandler(this);
}
wxLEAVE_CRIT_SECT( m_pendingEventsLock );
ProcessEvent(*event);
// 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
}
/* static */
bool wxEvtHandler::ProcessEventIfMatchesId(const wxEventTableEntryBase& entry,
wxEvtHandler *handler,
wxEvent& event)
{
int tableId1 = entry.m_id,
tableId2 = entry.m_lastId;
// match only if the event type is the same and the id is either -1 in
// the event table (meaning "any") or the event id matches the id
// specified in the event table either exactly or by falling into
// range between first and last
if ((tableId1 == wxID_ANY) ||
(tableId2 == wxID_ANY && tableId1 == event.GetId()) ||
(tableId2 != wxID_ANY &&
(event.GetId() >= tableId1 && event.GetId() <= tableId2)))
{
event.Skip(false);
event.m_callbackUserData = entry.m_callbackUserData;
#if wxUSE_EXCEPTIONS
if ( wxTheApp )
{
// call the handler via wxApp method which allows the user to catch
// any exceptions which may be thrown by any handler in the program
// in one place
wxTheApp->CallEventHandler(handler, *entry.m_fn, event);
}
else
#endif // wxUSE_EXCEPTIONS
{
(*entry.m_fn)(handler, event);
}
if (!event.GetSkipped())
return true;
}
return false;
}
bool wxEvtHandler::DoTryApp(wxEvent& event)
{
if ( wxTheApp && (this != wxTheApp) )
{
// Special case: don't pass wxEVT_IDLE to wxApp, since it'll always
// swallow it. wxEVT_IDLE is sent explicitly to wxApp so it will be
// processed appropriately via SearchEventTable.
if ( event.GetEventType() != wxEVT_IDLE )
{
if ( wxTheApp->ProcessEvent(event) )
return true;
}
}
return false;
}
bool wxEvtHandler::TryBefore(wxEvent& event)
{
#if WXWIN_COMPATIBILITY_2_8
// call the old virtual function to keep the code overriding it working
return TryValidator(event);
#else
wxUnusedVar(event);
return false;
#endif
}
bool wxEvtHandler::TryAfter(wxEvent& event)
{
// We only want to pass the window to the application object once even if
// there are several chained handlers. Ensure that this is what happens by
// only calling DoTryApp() if there is no next handler (which would do it).
//
// Notice that, unlike simply calling TryAfter() on the last handler in the
// chain only from ProcessEvent(), this also works with wxWindow object in
// the middle of the chain: its overridden TryAfter() will still be called
// and propagate the event upwards the window hierarchy even if it's not
// the last one in the chain (which, admittedly, shouldn't happen often).
if ( GetNextHandler() )
return GetNextHandler()->TryAfter(event);
// If this event is going to be processed in another handler next, don't
// pass it to wxTheApp now, it will be done from TryAfter() of this other
// handler.
if ( event.WillBeProcessedAgain() )
return false;
#if WXWIN_COMPATIBILITY_2_8
// as above, call the old virtual function for compatibility
return TryParent(event);
#else
return DoTryApp(event);
#endif
}
bool wxEvtHandler::ProcessEvent(wxEvent& event)
{
// The very first thing we do is to allow any registered filters to hook
// into event processing in order to globally pre-process all events.
//
// 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() )
{
for ( wxEventFilter* f = ms_filterList; f; f = f->m_next )
{
int rc = f->FilterEvent(event);
if ( rc != wxEventFilter::Event_Skip )
{
wxASSERT_MSG( rc == wxEventFilter::Event_Ignore ||
rc == wxEventFilter::Event_Processed,
"unexpected FilterEvent() return value" );
return rc != wxEventFilter::Event_Ignore;
}
//else: proceed normally
}
}
// Short circuit the event processing logic if we're requested to process
// this event in this handler only, see DoTryChain() for more details.
if ( event.ShouldProcessOnlyIn(this) )
return TryBeforeAndHere(event);
// Try to process the event in this handler itself.
if ( ProcessEventLocally(event) )
{
// It is possible that DoTryChain() called from ProcessEventLocally()
// returned true but the event was not really processed: this happens
// if a custom handler ignores the request to process the event in this
// handler only and in this case we should skip the post processing
// done in TryAfter() but still return the correct value ourselves to
// indicate whether we did or did not find a handler for this event.
return !event.GetSkipped();
}
// If we still didn't find a handler, propagate the event upwards the
// window chain and/or to the application object.
if ( TryAfter(event) )
return true;
// No handler found anywhere, bail out.
return false;
}
bool wxEvtHandler::ProcessEventLocally(wxEvent& event)
{
// Try the hooks which should be called before our own handlers and this
// handler itself first. Notice that we should not call ProcessEvent() on
// this one as we're already called from it, which explains why we do it
// here and not in DoTryChain()
return TryBeforeAndHere(event) || DoTryChain(event);
}
bool wxEvtHandler::DoTryChain(wxEvent& event)
{
for ( wxEvtHandler *h = GetNextHandler(); h; h = h->GetNextHandler() )
{
// We need to process this event at the level of this handler only
// right now, the pre-/post-processing was either already done by
// ProcessEvent() from which we were called or will be done by it when
// we return.
//
// However we must call ProcessEvent() and not TryHereOnly() because the
// existing code (including some in wxWidgets itself) expects the
// overridden ProcessEvent() in its custom event handlers pushed on a
// window to be called.
//
// So we must call ProcessEvent() but it must not do what it usually
// does. To resolve this paradox we set up a special flag inside the
// object itself to let ProcessEvent() know that it shouldn't do any
// pre/post-processing for this event if it gets it. Note that this
// only applies to this handler, if the event is passed to another one
// by explicitly calling its ProcessEvent(), pre/post-processing should
// be done as usual.
//
// Final complication is that if the implementation of ProcessEvent()
// called wxEvent::DidntHonourProcessOnlyIn() (as the gross hack that
// is wxScrollHelperEvtHandler::ProcessEvent() does) and ignored our
// request to process event in this handler only, we have to compensate
// for it by not processing the event further because this was already
// done by that rogue event handler.
wxEventProcessInHandlerOnly processInHandlerOnly(event, h);
if ( h->ProcessEvent(event) )
{
// Make sure "skipped" flag is not set as the event was really
// processed in this case. Normally it shouldn't be set anyhow but
// make sure just in case the user code does something strange.
event.Skip(false);
return true;
}
if ( !event.ShouldProcessOnlyIn(h) )
{
// Still return true to indicate that no further processing should
// be undertaken but ensure that "skipped" flag is set so that the
// caller knows that the event was not really processed.
event.Skip();
return true;
}
}
return false;
}
bool wxEvtHandler::TryHereOnly(wxEvent& event)
{
// 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;
// Then static per-class event tables
if ( GetEventHashTable().HandleEvent(event, this) )
return true;
// There is an implicit entry for async method calls processing in every
// event handler:
if ( event.GetEventType() == wxEVT_ASYNC_METHOD_CALL &&
event.GetEventObject() == this )
{
static_cast<wxAsyncMethodCallEvent&>(event).Execute();
return true;
}
// We don't have a handler for this event.
return false;
}
bool wxEvtHandler::SafelyProcessEvent(wxEvent& event)
{
#if wxUSE_EXCEPTIONS
try
{
#endif
return ProcessEvent(event);
#if wxUSE_EXCEPTIONS
}
catch ( ... )
{
WXConsumeException();
return false;
}
#endif // wxUSE_EXCEPTIONS
}
#if wxUSE_EXCEPTIONS
/* static */
void wxEvtHandler::WXConsumeException()
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
try
{
if ( !wxTheApp || !wxTheApp->OnExceptionInMainLoop() )
{
// If OnExceptionInMainLoop() returns false, we're supposed to exit
// the program and for this we need to exit the main loop, not the
// possibly nested one we're running right now.
if ( wxTheApp )
{
wxTheApp->ExitMainLoop();
}
else
{
// We must not continue running after an exception, unless
// explicitly requested, so if we can't ensure this in any
// other way, do it brutally like this.
wxAbort();
}
}
//else: continue running current event loop
}
catch ( ... )
{
// OnExceptionInMainLoop() threw, possibly rethrowing the same
// exception again. We have to deal with it here because we can't
// allow the exception to escape from the handling code, this will
// result in a crash at best (e.g. when using wxGTK as C++
// exceptions can't propagate through the C GTK+ code and corrupt
// the stack) and in something even more weird at worst (like
// exceptions completely disappearing into the void under some
// 64 bit versions of Windows).
if ( loop && !loop->IsYielding() )
loop->Exit();
// Give the application one last possibility to store the exception
// for rethrowing it later, when we get back to our code.
bool stored = false;
try
{
if ( wxTheApp )
stored = wxTheApp->StoreCurrentException();
}
catch ( ... )
{
// StoreCurrentException() really shouldn't throw, but if it
// did, take it as an indication that it didn't store it.
}
// If it didn't take it, just abort, at least like this we behave
// consistently everywhere.
if ( !stored )
{
try
{
if ( wxTheApp )
wxTheApp->OnUnhandledException();
}
catch ( ... )
{
// And OnUnhandledException() absolutely shouldn't throw,
// but we still must account for the possibility that it
// did. At least show some information about the exception
// in this case.
wxTheApp->wxAppConsoleBase::OnUnhandledException();
}
wxAbort();
}
}
}
#endif // wxUSE_EXCEPTIONS
bool wxEvtHandler::SearchEventTable(wxEventTable& table, wxEvent& event)
{
const wxEventType eventType = event.GetEventType();
for ( int i = 0; table.entries[i].m_fn != 0; i++ )
{
const wxEventTableEntry& entry = table.entries[i];
if ( eventType == entry.m_eventType )
{
if ( ProcessEventIfMatchesId(entry, this, event) )
return true;
}
}
return false;
}
void wxEvtHandler::DoBind(int id,
int lastId,
wxEventType eventType,
wxEventFunctor *func,
wxObject *userData)
{
wxDynamicEventTableEntry *entry =
new wxDynamicEventTableEntry(eventType, id, lastId, func, userData);
// Check if the derived class allows binding such event handlers.
if ( !OnDynamicBind(*entry) )
{
delete entry;
return;
}
if (!m_dynamicEvents)
m_dynamicEvents = new DynamicEvents;
// We prefer to push back the entry here and then iterate over the vector
// in reverse direction in GetNextDynamicEntry() as it's more efficient
// than inserting the element at the front.
m_dynamicEvents->push_back(entry);
// Make sure we get to know when a sink is destroyed
wxEvtHandler *eventSink = func->GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
if ( evtConnRef )
evtConnRef->IncRef( );
else
new wxEventConnectionRef(this, eventSink);
}
}
bool
wxEvtHandler::DoUnbind(int id,
int lastId,
wxEventType eventType,
const wxEventFunctor& func,
wxObject *userData)
{
if (!m_dynamicEvents)
return false;
size_t cookie;
for ( wxDynamicEventTableEntry* entry = GetFirstDynamicEntry(cookie);
entry;
entry = GetNextDynamicEntry(cookie) )
{
if ((entry->m_id == id) &&
((entry->m_lastId == lastId) || (lastId == wxID_ANY)) &&
((entry->m_eventType == eventType) || (eventType == wxEVT_NULL)) &&
entry->m_fn->IsMatching(func) &&
((entry->m_callbackUserData == userData) || !userData))
{
// Remove connection from tracker node (wxEventConnectionRef)
wxEvtHandler *eventSink = entry->m_fn->GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
if ( evtConnRef )
evtConnRef->DecRef();
}
delete entry->m_callbackUserData;
// We can't delete the entry from the vector if we're currently
// iterating over it. As we don't know whether we're or not, just
// null it for now and we will really erase it when we do finish
// iterating over it the next time.
//
// Notice that we rely on "cookie" being just the index into the
// vector, which is not guaranteed by our API, but here we can use
// this implementation detail.
(*m_dynamicEvents)[cookie] = NULL;
delete entry;
return true;
}
}
return false;
}
wxDynamicEventTableEntry*
wxEvtHandler::GetFirstDynamicEntry(size_t& cookie) const
{
if ( !m_dynamicEvents )
return NULL;
// The handlers are in LIFO order, so we must start at the end.
cookie = m_dynamicEvents->size();
return GetNextDynamicEntry(cookie);
}
wxDynamicEventTableEntry*
wxEvtHandler::GetNextDynamicEntry(size_t& cookie) const
{
// On entry here cookie is one greater than the index of the entry to
// return, so if it is 0, it means that there are no more entries.
while ( cookie )
{
// Otherwise return the element at the previous index, skipping any
// null elements which indicate removed entries.
wxDynamicEventTableEntry* const entry = m_dynamicEvents->at(--cookie);
if ( entry )
return entry;
}
return NULL;
}
bool wxEvtHandler::SearchDynamicEventTable( wxEvent& event )
{
wxCHECK_MSG( m_dynamicEvents, false,
wxT("caller should check that we have dynamic events") );
DynamicEvents& dynamicEvents = *m_dynamicEvents;
bool needToPruneDeleted = false;
// We can't use Get{First,Next}DynamicEntry() here as they hide the deleted
// but not yet pruned entries from the caller, but here we do want to know
// about them, so iterate directly. Remember to do it in the reverse order
// to honour the order of handlers connection.
for ( size_t n = dynamicEvents.size(); n; n-- )
{
wxDynamicEventTableEntry* const entry = dynamicEvents[n - 1];
if ( !entry )
{
// This entry must have been unbound at some time in the past, so
// skip it now and really remove it from the vector below, once we
// finish iterating.
needToPruneDeleted = true;
continue;
}
if ( event.GetEventType() == entry->m_eventType )
{
wxEvtHandler *handler = entry->m_fn->GetEvtHandler();
if ( !handler )
handler = this;
if ( ProcessEventIfMatchesId(*entry, handler, event) )
{
// It's important to skip pruning of the unbound event entries
// below because this object itself could have been deleted by
// the event handler making m_dynamicEvents a dangling pointer
// which can't be accessed any longer in the code below.
//
// In practice, it hopefully shouldn't be a problem to wait
// until we get an event that we don't handle before pruning
// because this should happen soon enough and even if it
// doesn't the worst possible outcome is slightly increased
// memory consumption while not skipping pruning can result in
// hard to reproduce (because they require the disconnection
// and deletion happen at the same time which is not always the
// case) crashes.
return true;
}
}
}
if ( needToPruneDeleted )
{
size_t nNew = 0;
for ( size_t n = 0; n != dynamicEvents.size(); n++ )
{
if ( dynamicEvents[n] )
dynamicEvents[nNew++] = dynamicEvents[n];
}
wxASSERT( nNew != dynamicEvents.size() );
dynamicEvents.resize(nNew);
}
return false;
}
void wxEvtHandler::DoSetClientObject( wxClientData *data )
{
wxASSERT_MSG( m_clientDataType != wxClientData_Void,
wxT("can't have both object and void client data") );
delete m_clientObject;
m_clientObject = data;
m_clientDataType = wxClientData_Object;
}
wxClientData *wxEvtHandler::DoGetClientObject() const
{
// it's not an error to call GetClientObject() on a window which doesn't
// have client data at all - NULL will be returned
wxASSERT_MSG( m_clientDataType != wxClientData_Void,
wxT("this window doesn't have object client data") );
return m_clientObject;
}
void wxEvtHandler::DoSetClientData( void *data )
{
wxASSERT_MSG( m_clientDataType != wxClientData_Object,
wxT("can't have both object and void client data") );
m_clientData = data;
m_clientDataType = wxClientData_Void;
}
void *wxEvtHandler::DoGetClientData() const
{
// it's not an error to call GetClientData() on a window which doesn't have
// client data at all - NULL will be returned
wxASSERT_MSG( m_clientDataType != wxClientData_Object,
wxT("this window doesn't have void client data") );
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
size_t cookie;
for ( wxDynamicEventTableEntry* entry = GetFirstDynamicEntry(cookie);
entry;
entry = GetNextDynamicEntry(cookie) )
{
if ( entry->m_fn->GetEvtHandler() == sink )
{
delete entry->m_callbackUserData;
delete entry;
// Just as in DoUnbind(), we use our knowledge of
// GetNextDynamicEntry() implementation here.
(*m_dynamicEvents)[cookie] = NULL;
}
}
}
#endif // wxUSE_BASE
#if wxUSE_GUI
// Find a window with the focus, that is also a descendant of the given window.
// This is used to determine the window to initially send commands to.
wxWindow* wxFindFocusDescendant(wxWindow* ancestor)
{
// Process events starting with the window with the focus, if any.
wxWindow* focusWin = wxWindow::FindFocus();
wxWindow* win = focusWin;
// Check if this is a descendant of this frame.
// If not, win will be set to NULL.
while (win)
{
if (win == ancestor)
break;
else
win = win->GetParent();
}
if (win == NULL)
focusWin = NULL;
return focusWin;
}
// ----------------------------------------------------------------------------
// wxEventBlocker
// ----------------------------------------------------------------------------
wxEventBlocker::wxEventBlocker(wxWindow *win, wxEventType type)
{
wxCHECK_RET(win, wxT("Null window given to wxEventBlocker"));
m_window = win;
Block(type);
m_window->PushEventHandler(this);
}
wxEventBlocker::~wxEventBlocker()
{
wxEvtHandler *popped = m_window->PopEventHandler(false);
wxCHECK_RET(popped == this,
wxT("Don't push other event handlers into a window managed by wxEventBlocker!"));
}
bool wxEventBlocker::ProcessEvent(wxEvent& event)
{
// should this event be blocked?
for ( size_t i = 0; i < m_eventsToBlock.size(); i++ )
{
wxEventType t = (wxEventType)m_eventsToBlock[i];
if ( t == wxEVT_ANY || t == event.GetEventType() )
return true; // yes, it should: mark this event as processed
}
return wxEvtHandler::ProcessEvent(event);
}
#endif // wxUSE_GUI