Make deleting toolbar from its own event handler work again

While it is not guaranteed in general that destroying a window from an
event handler for an event originating from this window itself works, it
did use to work in the case of wxToolBar and its event handlers. However
this stopped working since faffaaae29 as
it added a test using the now deleted object member fields after the
call to the event handler.

To fix this regression without reintroducing the bug fixed by that
commit (see #16762), add an ugly way to check if the toolbar is still
alive after a call to its event handler and do it explicitly after
OnLeftClick() returns.

Closes #17732.
This commit is contained in:
Vadim Zeitlin
2017-10-27 14:49:44 +02:00
parent 266152b459
commit 1417776d33

View File

@@ -125,6 +125,14 @@ wxBEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
EVT_ERASE_BACKGROUND(wxToolBar::OnEraseBackground)
wxEND_EVENT_TABLE()
// ----------------------------------------------------------------------------
// module globals
// ----------------------------------------------------------------------------
// This is used to check if the toolbar itself doesn't get destroyed while
// handling its event.
static wxToolBar* gs_liveToolbar = NULL;
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
@@ -505,6 +513,10 @@ void wxToolBar::Recreate()
wxToolBar::~wxToolBar()
{
// Indicate to the code in MSWCommand() that the toolbar is destroyed.
if ( gs_liveToolbar == this )
gs_liveToolbar = NULL;
// we must refresh the frame size when the toolbar is deleted but the frame
// is not - otherwise toolbar leaves a hole in the place it used to occupy
SendSizeEventToParent();
@@ -1455,8 +1467,23 @@ bool wxToolBar::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD id_)
::SendMessage(GetHwnd(), TB_SETSTATE, id, MAKELONG(state | TBSTATE_PRESSED, 0));
Update();
// Before calling the event handler, store a pointer to this toolbar in the
// global variable: if it gets reset from our dtor, we will know that the
// toolbar was destroyed by this handler and that we can't use this object
// any more.
gs_liveToolbar = this;
bool allowLeftClick = OnLeftClick(id, toggled);
if ( gs_liveToolbar != this )
{
// Bail out, we can't touch any member fields in the already
// destroyed object anyhow.
return true;
}
gs_liveToolbar = NULL;
// Check if the tool hasn't been deleted in the event handler (notice that
// it's also possible that this tool was deleted and a new tool with the
// same ID was created, so we really need to check if the pointer to the