Fix infinite recursion if wxGrid is modified from event handler

Calling wxGrid::{Insert,Delete}{Rows,Cols}() from wxEVT_GRID_CELL_CHANGE
event handler resulted in infinite recursion because it tried to hide
the grid editor control again, which resulted in another CELL_CHANGE
event being generated and so on.

Break this infinite recursion in the usual way, i.e. by updating the
state of wxGrid before invoking the user-defined event handler.

This required separating SaveEditControlValue() in 2 functions, the main
one retaining IsCellEditControlEnabled() check for compatibility, and
the new DoSaveEditControlValue() that can be called even after disabling
the editor.

Closes #2287.

Closes https://github.com/wxWidgets/wxWidgets/pull/1540
This commit is contained in:
Vadim Zeitlin 2019-09-08 23:01:13 +02:00
parent c1fdc717ae
commit be53b3fb20
2 changed files with 43 additions and 30 deletions

View File

@ -2482,6 +2482,10 @@ private:
void SetNativeHeaderColCount();
void SetNativeHeaderColOrder();
// Unlike the public SaveEditControlValue(), this method doesn't check if
// the edit control is shown, but just supposes that it is.
void DoSaveEditControlValue();
// these sets contain the indices of fixed, i.e. non-resizable
// interactively, grid rows or columns and are NULL if there are no fixed
// elements (which is the default)

View File

@ -6858,10 +6858,14 @@ void wxGrid::EnableCellEditControl( bool enable )
SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
HideCellEditControl();
SaveEditControlValue();
// do it after HideCellEditControl()
m_cellEditCtrlEnabled = enable;
// do it after HideCellEditControl() but before invoking
// user-defined handlers invoked by DoSaveEditControlValue() to
// ensure that we don't enter infinite loop if any of them try to
// disable the edit control again.
m_cellEditCtrlEnabled = false;
DoSaveEditControlValue();
}
}
}
@ -7115,36 +7119,41 @@ void wxGrid::SaveEditControlValue()
{
if ( IsCellEditControlEnabled() )
{
int row = m_currentCellCoords.GetRow();
int col = m_currentCellCoords.GetCol();
wxString oldval = GetCellValue(row, col);
wxGridCellAttr* attr = GetCellAttr(row, col);
wxGridCellEditor* editor = attr->GetEditor(this, row, col);
wxString newval;
bool changed = editor->EndEdit(row, col, this, oldval, &newval);
if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 )
{
editor->ApplyEdit(row, col, this);
// for compatibility reasons dating back to wx 2.8 when this event
// was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
// didn't exist we allow vetoing this one too
if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
{
// Event has been vetoed, set the data back.
SetCellValue(row, col, oldval);
}
}
editor->DecRef();
attr->DecRef();
DoSaveEditControlValue();
}
}
void wxGrid::DoSaveEditControlValue()
{
int row = m_currentCellCoords.GetRow();
int col = m_currentCellCoords.GetCol();
wxString oldval = GetCellValue(row, col);
wxGridCellAttr* attr = GetCellAttr(row, col);
wxGridCellEditor* editor = attr->GetEditor(this, row, col);
wxString newval;
bool changed = editor->EndEdit(row, col, this, oldval, &newval);
if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 )
{
editor->ApplyEdit(row, col, this);
// for compatibility reasons dating back to wx 2.8 when this event
// was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
// didn't exist we allow vetoing this one too
if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
{
// Event has been vetoed, set the data back.
SetCellValue(row, col, oldval);
}
}
editor->DecRef();
attr->DecRef();
}
void wxGrid::OnHideEditor(wxCommandEvent& WXUNUSED(event))
{
DisableCellEditControl();