Merge branch 'grid-streamline'

Various simplifications and bug fixes in wxGrid code, mostly related to
handling in-place cell editing.

See https://github.com/wxWidgets/wxWidgets/pull/1910
This commit is contained in:
Vadim Zeitlin
2020-06-30 00:25:46 +02:00
3 changed files with 339 additions and 330 deletions

View File

@@ -1504,12 +1504,12 @@ public:
void EnableCellEditControl( bool enable = true ); void EnableCellEditControl( bool enable = true );
void DisableCellEditControl() { EnableCellEditControl(false); } void DisableCellEditControl() { EnableCellEditControl(false); }
bool CanEnableCellControl() const; bool CanEnableCellControl() const;
bool IsCellEditControlEnabled() const; bool IsCellEditControlEnabled() const { return m_cellEditCtrlEnabled; }
bool IsCellEditControlShown() const; bool IsCellEditControlShown() const;
bool IsCurrentCellReadOnly() const; bool IsCurrentCellReadOnly() const;
void ShowCellEditControl(); void ShowCellEditControl(); // Use EnableCellEditControl() instead.
void HideCellEditControl(); void HideCellEditControl();
void SaveEditControlValue(); void SaveEditControlValue();
@@ -2520,7 +2520,6 @@ protected:
wxGridCellAttr* m_defaultCellAttr; wxGridCellAttr* m_defaultCellAttr;
bool m_inOnKeyDown;
int m_batchCount; int m_batchCount;
@@ -2897,6 +2896,36 @@ private:
void SetNativeHeaderColCount(); void SetNativeHeaderColCount();
void SetNativeHeaderColOrder(); void SetNativeHeaderColOrder();
// Return the editor which should be used for the current cell.
wxGridCellEditorPtr GetCurrentCellEditorPtr() const
{
return GetCellAttrPtr(m_currentCellCoords)->GetEditorPtr
(
this,
m_currentCellCoords.GetRow(),
m_currentCellCoords.GetCol()
);
}
// Show/hide the cell editor for the current cell unconditionally.
void DoShowCellEditControl();
void DoHideCellEditControl();
// Unconditionally try showing the editor for the current cell.
//
// Returns false if the user code vetoed wxEVT_GRID_EDITOR_SHOWN.
bool DoEnableCellEditControl();
// Unconditionally disable (accepting the changes) the editor.
void DoDisableCellEditControl();
// Accept the changes in the edit control, i.e. save them to the table and
// dismiss the editor. Also reset m_cellEditCtrlEnabled.
void DoAcceptCellEditControl();
// As above, but do nothing if the control is not currently shown.
void AcceptCellEditControlIfShown();
// Unlike the public SaveEditControlValue(), this method doesn't check if // Unlike the public SaveEditControlValue(), this method doesn't check if
// the edit control is shown, but just supposes that it is. // the edit control is shown, but just supposes that it is.
void DoSaveEditControlValue(); void DoSaveEditControlValue();

View File

@@ -3437,9 +3437,12 @@ public:
allows the user to change the cell value. allows the user to change the cell value.
Disabling in-place editing does nothing if the in-place editor isn't Disabling in-place editing does nothing if the in-place editor isn't
currently show, otherwise the @c wxEVT_GRID_EDITOR_HIDDEN event is currently shown, otherwise the @c wxEVT_GRID_EDITOR_HIDDEN event is
generated but, unlike the "shown" event, it can't be vetoed and the generated but, unlike the "shown" event, it can't be vetoed and the
in-place editor is dismissed unconditionally. in-place editor is dismissed unconditionally.
Note that it is an error to call this function if the current cell is
read-only, use CanEnableCellControl() to check for this precondition.
*/ */
void EnableCellEditControl(bool enable = true); void EnableCellEditControl(bool enable = true);
@@ -3824,8 +3827,9 @@ public:
Displays the active in-place cell edit control for the current cell Displays the active in-place cell edit control for the current cell
after it was hidden. after it was hidden.
Note that this method does @em not start editing the cell, this is only This method should only be called after calling HideCellEditControl(),
done by EnableCellEditControl(). to start editing the current grid cell use EnableCellEditControl()
instead.
*/ */
void ShowCellEditControl(); void ShowCellEditControl();

View File

@@ -2012,7 +2012,7 @@ void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event )
void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event ) void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event )
{ {
if (!m_owner->GetEventHandler()->ProcessEvent( event )) if (!m_owner->ProcessWindowEvent( event ))
event.Skip(); event.Skip();
} }
@@ -2061,7 +2061,7 @@ void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event )
void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event )
{ {
if (!m_owner->GetEventHandler()->ProcessEvent( event )) if (!m_owner->ProcessWindowEvent( event ))
event.Skip(); event.Skip();
} }
@@ -2087,7 +2087,7 @@ void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event )
void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event ) void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event )
{ {
if (!m_owner->GetEventHandler()->ProcessEvent(event)) if (!m_owner->ProcessWindowEvent(event))
event.Skip(); event.Skip();
} }
@@ -2450,7 +2450,7 @@ void wxGridWindow::OnMouseEvent( wxMouseEvent& event )
void wxGridWindow::OnMouseWheel( wxMouseEvent& event ) void wxGridWindow::OnMouseWheel( wxMouseEvent& event )
{ {
if (!m_owner->GetEventHandler()->ProcessEvent( event )) if (!m_owner->ProcessWindowEvent( event ))
event.Skip(); event.Skip();
} }
@@ -2459,19 +2459,19 @@ void wxGridWindow::OnMouseWheel( wxMouseEvent& event )
// //
void wxGridWindow::OnKeyDown( wxKeyEvent& event ) void wxGridWindow::OnKeyDown( wxKeyEvent& event )
{ {
if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) if ( !m_owner->ProcessWindowEvent( event ) )
event.Skip(); event.Skip();
} }
void wxGridWindow::OnKeyUp( wxKeyEvent& event ) void wxGridWindow::OnKeyUp( wxKeyEvent& event )
{ {
if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) if ( !m_owner->ProcessWindowEvent( event ) )
event.Skip(); event.Skip();
} }
void wxGridWindow::OnChar( wxKeyEvent& event ) void wxGridWindow::OnChar( wxKeyEvent& event )
{ {
if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) if ( !m_owner->ProcessWindowEvent( event ) )
event.Skip(); event.Skip();
} }
@@ -2501,7 +2501,7 @@ void wxGridWindow::OnFocus(wxFocusEvent& event)
Refresh(true, &cursor); Refresh(true, &cursor);
} }
if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) if ( !m_owner->ProcessWindowEvent( event ) )
event.Skip(); event.Skip();
} }
@@ -2887,7 +2887,6 @@ void wxGrid::Init()
m_editable = true; // default for whole grid m_editable = true; // default for whole grid
m_inOnKeyDown = false;
m_batchCount = 0; m_batchCount = 0;
m_extraWidth = m_extraWidth =
@@ -3015,13 +3014,7 @@ void wxGrid::CalcDimensions()
// take into account editor if shown // take into account editor if shown
if ( IsCellEditControlShown() ) if ( IsCellEditControlShown() )
{ {
int r = m_currentCellCoords.GetRow(); const wxRect rect = GetCurrentCellEditorPtr()->GetWindow()->GetRect();
int c = m_currentCellCoords.GetCol();
// how big is the editor
wxGridCellAttrPtr attr = GetCellAttrPtr(r, c);
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, r, c);
const wxRect rect = editor->GetWindow()->GetRect();
if ( rect.GetRight() > w ) if ( rect.GetRight() > w )
w = rect.GetRight(); w = rect.GetRight();
if ( rect.GetBottom() > h ) if ( rect.GetBottom() > h )
@@ -3716,6 +3709,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
row = YToEdgeOfRow(pos.y); row = YToEdgeOfRow(pos.y);
if ( row != wxNOT_FOUND && CanDragRowSize(row) ) if ( row != wxNOT_FOUND && CanDragRowSize(row) )
{ {
DoStartResizeRowOrCol(row);
ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin); ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin);
} }
else // not a request to start resizing else // not a request to start resizing
@@ -3853,7 +3847,6 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
{ {
if ( CanDragRowSize(dragRowOrCol) ) if ( CanDragRowSize(dragRowOrCol) )
{ {
DoStartResizeRowOrCol(dragRowOrCol);
ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin, false); ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin, false);
} }
} }
@@ -3972,11 +3965,7 @@ void wxGrid::DoStartResizeRowOrCol(int col)
{ {
// Hide the editor if it's currently shown to avoid any weird interactions // Hide the editor if it's currently shown to avoid any weird interactions
// with it while dragging the row/column separator. // with it while dragging the row/column separator.
if ( IsCellEditControlShown() ) AcceptCellEditControlIfShown();
{
HideCellEditControl();
SaveEditControlValue();
}
m_dragRowOrCol = col; m_dragRowOrCol = col;
} }
@@ -4117,6 +4106,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
int colEdge = XToEdgeOfCol(x); int colEdge = XToEdgeOfCol(x);
if ( colEdge != wxNOT_FOUND && CanDragColSize(colEdge) ) if ( colEdge != wxNOT_FOUND && CanDragColSize(colEdge) )
{ {
DoStartResizeRowOrCol(colEdge);
ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin); ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin);
} }
else // not a request to start resizing else // not a request to start resizing
@@ -4308,7 +4298,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
{ {
if ( CanDragColSize(dragRowOrCol) ) if ( CanDragColSize(dragRowOrCol) )
{ {
DoStartResizeRowOrCol(dragRowOrCol);
ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin, false); ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin, false);
} }
} }
@@ -4487,11 +4476,7 @@ wxGrid::DoGridCellDrag(wxMouseEvent& event,
if ( isFirstDrag ) if ( isFirstDrag )
{ {
// Hide the edit control, so it won't interfere with drag-shrinking. // Hide the edit control, so it won't interfere with drag-shrinking.
if ( IsCellEditControlShown() ) AcceptCellEditControlIfShown();
{
HideCellEditControl();
SaveEditControlValue();
}
switch ( event.GetModifiers() ) switch ( event.GetModifiers() )
{ {
@@ -4580,11 +4565,18 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event,
// it being disabled for a particular row/column as it would be // it being disabled for a particular row/column as it would be
// surprising to have different mouse behaviour in different parts of // surprising to have different mouse behaviour in different parts of
// the same grid, so we only check for it being globally disabled). // the same grid, so we only check for it being globally disabled).
if ( CanDragGridColEdges() && XToEdgeOfCol(pos.x) != wxNOT_FOUND ) int dragRowOrCol = wxNOT_FOUND;
return; if ( CanDragGridColEdges() )
dragRowOrCol = XToEdgeOfCol(pos.x);
if ( CanDragGridRowEdges() && YToEdgeOfRow(pos.y) != wxNOT_FOUND ) if ( dragRowOrCol == wxNOT_FOUND && CanDragGridRowEdges() )
dragRowOrCol = YToEdgeOfRow(pos.y);
if ( dragRowOrCol != wxNOT_FOUND )
{
DoStartResizeRowOrCol(dragRowOrCol);
return; return;
}
DisableCellEditControl(); DisableCellEditControl();
MakeCellVisible( coords ); MakeCellVisible( coords );
@@ -4671,20 +4663,12 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event,
if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() )
{ {
ClearSelection(); ClearSelection();
EnableCellEditControl();
wxGridCellAttrPtr attr = GetCellAttrPtr(coords); if ( DoEnableCellEditControl() )
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, coords.GetRow(), coords.GetCol()); GetCurrentCellEditorPtr()->StartingClick();
editor->StartingClick();
m_waitForSlowClick = false; m_waitForSlowClick = false;
} }
else if ( m_selection && m_selection->IsSelection() )
{
// Show the edit control, if it has been hidden for
// drag-shrinking.
ShowCellEditControl();
}
} }
else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
{ {
@@ -4723,7 +4707,6 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event),
{ {
if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
{ {
DoStartResizeRowOrCol(dragCol);
ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, gridWindow, false); ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, gridWindow, false);
} }
} }
@@ -4731,7 +4714,6 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event),
{ {
if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
{ {
DoStartResizeRowOrCol(dragRow);
ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false); ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false);
} }
} }
@@ -4764,8 +4746,8 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG
wxGridCellCoords coords = XYToCell(pos, gridWindow); wxGridCellCoords coords = XYToCell(pos, gridWindow);
int cell_rows, cell_cols; int cell_rows, cell_cols;
GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols ); if ( GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols )
if ( (cell_rows < 0) || (cell_cols < 0) ) == CellSpan_Inside )
{ {
coords.SetRow(coords.GetRow() + cell_rows); coords.SetRow(coords.GetRow() + cell_rows);
coords.SetCol(coords.GetCol() + cell_cols); coords.SetCol(coords.GetCol() + cell_cols);
@@ -5244,8 +5226,7 @@ void wxGrid::ClearGrid()
{ {
if ( m_table ) if ( m_table )
{ {
if (IsCellEditControlEnabled()) DisableCellEditControl();
DisableCellEditControl();
m_table->Clear(); m_table->Clear();
if ( ShouldRefresh() ) if ( ShouldRefresh() )
@@ -5262,8 +5243,7 @@ wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t),
if ( !m_table ) if ( !m_table )
return false; return false;
if ( IsCellEditControlEnabled() ) DisableCellEditControl();
DisableCellEditControl();
return (m_table->*funcModify)(pos, num); return (m_table->*funcModify)(pos, num);
@@ -5302,7 +5282,7 @@ wxGrid::SendGridSizeEvent(wxEventType type,
mouseEv.GetY() + GetColLabelSize(), mouseEv.GetY() + GetColLabelSize(),
mouseEv); mouseEv);
return GetEventHandler()->ProcessEvent(gridEvt); return ProcessWindowEvent(gridEvt);
} }
// Process the event and return // Process the event and return
@@ -5311,7 +5291,7 @@ wxGrid::SendGridSizeEvent(wxEventType type,
// 0 if the event wasn't handled // 0 if the event wasn't handled
int wxGrid::DoSendEvent(wxGridEvent& gridEvt) int wxGrid::DoSendEvent(wxGridEvent& gridEvt)
{ {
const bool claimed = GetEventHandler()->ProcessEvent(gridEvt); const bool claimed = ProcessWindowEvent(gridEvt);
// A Veto'd event may not be `claimed' so test this first // A Veto'd event may not be `claimed' so test this first
if ( !gridEvt.IsAllowed() ) if ( !gridEvt.IsAllowed() )
@@ -5658,21 +5638,12 @@ void wxGrid::OnDPIChanged(wxDPIChangedEvent& event)
void wxGrid::OnKeyDown( wxKeyEvent& event ) void wxGrid::OnKeyDown( wxKeyEvent& event )
{ {
if ( m_inOnKeyDown )
{
// shouldn't be here - we are going round in circles...
//
wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
}
m_inOnKeyDown = true;
// propagate the event up and see if it gets processed // propagate the event up and see if it gets processed
wxWindow *parent = GetParent(); wxWindow *parent = GetParent();
wxKeyEvent keyEvt( event ); wxKeyEvent keyEvt( event );
keyEvt.SetEventObject( parent ); keyEvt.SetEventObject( parent );
if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) ) if ( !parent->ProcessWindowEvent( keyEvt ) )
{ {
if (GetLayoutDirection() == wxLayout_RightToLeft) if (GetLayoutDirection() == wxLayout_RightToLeft)
{ {
@@ -5725,13 +5696,14 @@ void wxGrid::OnKeyDown( wxKeyEvent& event )
} }
else else
{ {
if ( !MoveCursorDown( event.ShiftDown() ) ) // We want to accept the changes in the editor when Enter
{ // is pressed in any case, so do it (note that in many
// Normally this would be done by MoveCursorDown(), but // cases this would be done by MoveCursorDown() itself, but
// if it failed to move the cursor, e.g. because we're // not always, e.g. it wouldn't do it when editing the
// at the bottom of a column, do it here. // cells in the last row or when using Shift-Enter).
DisableCellEditControl(); DisableCellEditControl();
}
MoveCursorDown( event.ShiftDown() );
} }
break; break;
@@ -5973,8 +5945,6 @@ void wxGrid::OnKeyDown( wxKeyEvent& event )
break; break;
} }
} }
m_inOnKeyDown = false;
} }
void wxGrid::OnKeyUp( wxKeyEvent& WXUNUSED(event) ) void wxGrid::OnKeyUp( wxKeyEvent& WXUNUSED(event) )
@@ -5988,25 +5958,18 @@ void wxGrid::OnChar( wxKeyEvent& event )
if ( !IsCellEditControlEnabled() && CanEnableCellControl() ) if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
{ {
// yes, now check whether the cells editor accepts the key // yes, now check whether the cells editor accepts the key
int row = m_currentCellCoords.GetRow(); wxGridCellEditorPtr editor = GetCurrentCellEditorPtr();
int col = m_currentCellCoords.GetCol();
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col);
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col);
// <F2> is special and will always start editing, for // <F2> is special and will always start editing, for
// other keys - ask the editor itself // other keys - ask the editor itself
if ( (event.GetKeyCode() == WXK_F2 && !event.HasModifiers()) const bool specialEditKey = event.GetKeyCode() == WXK_F2 &&
|| editor->IsAcceptedKey(event) ) !event.HasModifiers();
if ( specialEditKey || editor->IsAcceptedKey(event) )
{ {
// ensure cell is visble // ensure cell is visble
MakeCellVisible(row, col); MakeCellVisible(m_currentCellCoords);
EnableCellEditControl();
// a problem can arise if the cell is not completely if ( DoEnableCellEditControl() && !specialEditKey )
// visible (even after calling MakeCellVisible the
// control is not created and calling StartingKey will
// crash the app
if ( event.GetKeyCode() != WXK_F2 && editor->IsCreated() && m_cellEditCtrlEnabled )
editor->StartingKey(event); editor->StartingKey(event);
} }
else else
@@ -6166,10 +6129,9 @@ void wxGrid::DrawGridCellArea( wxDC& dc, const wxGridCellCoordsArray& cells )
int row, col, cell_rows, cell_cols; int row, col, cell_rows, cell_cols;
row = cells[i].GetRow(); row = cells[i].GetRow();
col = cells[i].GetCol(); col = cells[i].GetCol();
GetCellSize( row, col, &cell_rows, &cell_cols );
// If this cell is part of a multicell block, find owner for repaint // If this cell is part of a multicell block, find owner for repaint
if ( cell_rows <= 0 || cell_cols <= 0 ) if ( GetCellSize( row, col, &cell_rows, &cell_cols ) == CellSpan_Inside )
{ {
wxGridCellCoords cell( row + cell_rows, col + cell_cols ); wxGridCellCoords cell( row + cell_rows, col + cell_cols );
bool marked = false; bool marked = false;
@@ -6537,26 +6499,31 @@ wxGrid::DrawRangeGridLines(wxDC& dc,
for ( int col = topLeft.GetCol(); col <= bottomRight.GetCol(); col++ ) for ( int col = topLeft.GetCol(); col <= bottomRight.GetCol(); col++ )
{ {
int cell_rows, cell_cols; int cell_rows, cell_cols;
GetCellSize( row, col, &cell_rows, &cell_cols ); switch ( GetCellSize( row, col, &cell_rows, &cell_cols ) )
if ( cell_rows > 1 || cell_cols > 1 ) // multi cell
{ {
rect = CellToRect( row, col ); case CellSpan_Main: // multi cell
// cater for scaling rect = CellToRect( row, col );
// device origin already set in ::Render() for x, y // cater for scaling
rect.x = dc.LogicalToDeviceX( rect.x ); // device origin already set in ::Render() for x, y
rect.y = dc.LogicalToDeviceY( rect.y ); rect.x = dc.LogicalToDeviceX( rect.x );
rect.width = dc.LogicalToDeviceXRel( rect.width ); rect.y = dc.LogicalToDeviceY( rect.y );
rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1; rect.width = dc.LogicalToDeviceXRel( rect.width );
clippedcells.Subtract( rect ); rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1;
} clippedcells.Subtract( rect );
else if ( cell_rows < 0 || cell_cols < 0 ) // part of multicell break;
{
rect = CellToRect( row + cell_rows, col + cell_cols ); case CellSpan_Inside: // part of multicell
rect.x = dc.LogicalToDeviceX( rect.x ); rect = CellToRect( row + cell_rows, col + cell_cols );
rect.y = dc.LogicalToDeviceY( rect.y ); rect.x = dc.LogicalToDeviceX( rect.x );
rect.width = dc.LogicalToDeviceXRel( rect.width ); rect.y = dc.LogicalToDeviceY( rect.y );
rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1; rect.width = dc.LogicalToDeviceXRel( rect.width );
clippedcells.Subtract( rect ); rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1;
clippedcells.Subtract( rect );
break;
case CellSpan_None:
// Nothing special to do.
break;
} }
} }
} }
@@ -6628,20 +6595,25 @@ void wxGrid::DrawAllGridWindowLines(wxDC& dc, const wxRegion & WXUNUSED(reg), wx
{ {
int i = GetColAt( colPos ); int i = GetColAt( colPos );
GetCellSize( j, i, &cell_rows, &cell_cols ); switch ( GetCellSize( j, i, &cell_rows, &cell_cols ) )
if ((cell_rows > 1) || (cell_cols > 1))
{ {
rect = CellToRect(j,i); case CellSpan_Main:
rect.Offset(-gridOffset); rect = CellToRect(j,i);
CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); rect.Offset(-gridOffset);
clippedcells.Subtract(rect); CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
} clippedcells.Subtract(rect);
else if ((cell_rows < 0) || (cell_cols < 0)) break;
{
rect = CellToRect(j + cell_rows, i + cell_cols); case CellSpan_Inside:
rect.Offset(-gridOffset); rect = CellToRect(j + cell_rows, i + cell_cols);
CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); rect.Offset(-gridOffset);
clippedcells.Subtract(rect); CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
clippedcells.Subtract(rect);
break;
case CellSpan_None:
// Nothing special to do.
break;
} }
} }
} }
@@ -7174,34 +7146,37 @@ void wxGrid::EnableCellEditControl( bool enable )
{ {
if ( enable ) if ( enable )
{ {
if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
return;
// this should be checked by the caller! // this should be checked by the caller!
wxASSERT_MSG( CanEnableCellControl(), wxT("can't enable editing for this cell!") ); wxCHECK_RET( CanEnableCellControl(), wxT("can't enable editing for this cell!") );
// do it before ShowCellEditControl() DoEnableCellEditControl();
m_cellEditCtrlEnabled = enable;
ShowCellEditControl();
} }
else else
{ {
SendEvent(wxEVT_GRID_EDITOR_HIDDEN); DoDisableCellEditControl();
HideCellEditControl();
// 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();
} }
} }
} }
bool wxGrid::DoEnableCellEditControl()
{
if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
return false;
m_cellEditCtrlEnabled = true;
DoShowCellEditControl();
return true;
}
void wxGrid::DoDisableCellEditControl()
{
SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
DoAcceptCellEditControl();
}
bool wxGrid::IsCurrentCellReadOnly() const bool wxGrid::IsCurrentCellReadOnly() const
{ {
return const_cast<wxGrid *>(this)-> return const_cast<wxGrid *>(this)->
@@ -7214,23 +7189,13 @@ bool wxGrid::CanEnableCellControl() const
!IsCurrentCellReadOnly(); !IsCurrentCellReadOnly();
} }
bool wxGrid::IsCellEditControlEnabled() const
{
// the cell edit control might be disable for all cells or just for the
// current one if it's read only
return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : false;
}
bool wxGrid::IsCellEditControlShown() const bool wxGrid::IsCellEditControlShown() const
{ {
bool isShown = false; bool isShown = false;
if ( m_cellEditCtrlEnabled ) if ( m_cellEditCtrlEnabled )
{ {
int row = m_currentCellCoords.GetRow(); if ( wxGridCellEditorPtr editor = GetCurrentCellEditorPtr() )
int col = m_currentCellCoords.GetCol();
wxGridCellEditorPtr editor = GetCellAttrPtr(row, col)->GetEditorPtr(this, row, col);
if ( editor )
{ {
if ( editor->IsCreated() ) if ( editor->IsCreated() )
{ {
@@ -7251,192 +7216,215 @@ void wxGrid::ShowCellEditControl()
m_cellEditCtrlEnabled = false; m_cellEditCtrlEnabled = false;
return; return;
} }
else
{
wxRect rect = CellToRect( m_currentCellCoords );
int row = m_currentCellCoords.GetRow();
int col = m_currentCellCoords.GetCol();
wxGridWindow *gridWindow = CellToGridWindow(row, col); DoShowCellEditControl();
}
}
// if this is part of a multicell, find owner (topleft) void wxGrid::DoShowCellEditControl()
int cell_rows, cell_cols; {
GetCellSize( row, col, &cell_rows, &cell_cols ); wxRect rect = CellToRect( m_currentCellCoords );
if ( cell_rows <= 0 || cell_cols <= 0 ) int row = m_currentCellCoords.GetRow();
{ int col = m_currentCellCoords.GetCol();
row += cell_rows;
col += cell_cols;
m_currentCellCoords.SetRow( row );
m_currentCellCoords.SetCol( col );
}
rect.Offset(-GetGridWindowOffset(gridWindow)); wxGridWindow *gridWindow = CellToGridWindow(row, col);
// convert to scrolled coords // if this is part of a multicell, find owner (topleft)
CalcGridWindowScrolledPosition( rect.x, rect.y, &rect.x, &rect.y, gridWindow ); int cell_rows, cell_cols;
if ( GetCellSize( row, col, &cell_rows, &cell_cols ) == CellSpan_Inside )
{
row += cell_rows;
col += cell_cols;
m_currentCellCoords.SetRow( row );
m_currentCellCoords.SetCol( col );
}
rect.Offset(-GetGridWindowOffset(gridWindow));
// convert to scrolled coords
CalcGridWindowScrolledPosition( rect.x, rect.y, &rect.x, &rect.y, gridWindow );
#ifdef __WXQT__ #ifdef __WXQT__
// Substract 1 pixel in every dimension to fit in the cell area. // Substract 1 pixel in every dimension to fit in the cell area.
// If not, Qt will draw the control outside the cell. // If not, Qt will draw the control outside the cell.
// TODO: Check offsets under Qt. // TODO: Check offsets under Qt.
rect.Deflate(1, 1); rect.Deflate(1, 1);
#endif #endif
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col); wxGridCellAttrPtr attr = GetCellAttrPtr(row, col);
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col); wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col);
if ( !editor->IsCreated() ) if ( !editor->IsCreated() )
{
editor->Create(gridWindow, wxID_ANY,
new wxGridCellEditorEvtHandler(this, editor.get()));
// Ensure the editor window has wxWANTS_CHARS flag, so that it
// gets Tab, Enter and Esc keys, which need to be processed
// specially by wxGridCellEditorEvtHandler.
wxWindow* const editorWindow = editor->GetWindow();
if ( editorWindow )
{
editorWindow->SetWindowStyle(editorWindow->GetWindowStyle()
| wxWANTS_CHARS);
}
wxGridEditorCreatedEvent evt(GetId(),
wxEVT_GRID_EDITOR_CREATED,
this,
row,
col,
editorWindow);
ProcessWindowEvent(evt);
}
else if ( editor->GetWindow() &&
editor->GetWindow()->GetParent() != gridWindow )
{
editor->GetWindow()->Reparent(gridWindow);
}
// resize editor to overflow into righthand cells if allowed
int maxWidth = rect.width;
wxString value = GetCellValue(row, col);
if ( !value.empty() && attr->GetOverflow() )
{
int y;
GetTextExtent(value, &maxWidth, &y, NULL, NULL, &attr->GetFont());
if (maxWidth < rect.width)
maxWidth = rect.width;
}
if ((maxWidth > rect.width) && (col < m_numCols) && m_table)
{
GetCellSize( row, col, &cell_rows, &cell_cols );
// may have changed earlier
for (int i = col + cell_cols; i < m_numCols; i++)
{
int c_rows, c_cols;
GetCellSize( row, i, &c_rows, &c_cols );
// looks weird going over a multicell
if (m_table->IsEmptyCell( row, i ) &&
(rect.width < maxWidth) && (c_rows == 1))
{ {
editor->Create(gridWindow, wxID_ANY, rect.width += GetColWidth( i );
new wxGridCellEditorEvtHandler(this, editor.get()));
// Ensure the editor window has wxWANTS_CHARS flag, so that it
// gets Tab, Enter and Esc keys, which need to be processed
// specially by wxGridCellEditorEvtHandler.
wxWindow* const editorWindow = editor->GetWindow();
if ( editorWindow )
{
editorWindow->SetWindowStyle(editorWindow->GetWindowStyle()
| wxWANTS_CHARS);
}
wxGridEditorCreatedEvent evt(GetId(),
wxEVT_GRID_EDITOR_CREATED,
this,
row,
col,
editorWindow);
GetEventHandler()->ProcessEvent(evt);
} }
else if ( editor->GetWindow() && else
editor->GetWindow()->GetParent() != gridWindow ) break;
{
editor->GetWindow()->Reparent(gridWindow);
}
// resize editor to overflow into righthand cells if allowed
int maxWidth = rect.width;
wxString value = GetCellValue(row, col);
if ( !value.empty() && attr->GetOverflow() )
{
int y;
GetTextExtent(value, &maxWidth, &y, NULL, NULL, &attr->GetFont());
if (maxWidth < rect.width)
maxWidth = rect.width;
}
if ((maxWidth > rect.width) && (col < m_numCols) && m_table)
{
GetCellSize( row, col, &cell_rows, &cell_cols );
// may have changed earlier
for (int i = col + cell_cols; i < m_numCols; i++)
{
int c_rows, c_cols;
GetCellSize( row, i, &c_rows, &c_cols );
// looks weird going over a multicell
if (m_table->IsEmptyCell( row, i ) &&
(rect.width < maxWidth) && (c_rows == 1))
{
rect.width += GetColWidth( i );
}
else
break;
}
}
editor->SetCellAttr( attr.get() );
editor->SetSize( rect );
// Note that the actual rectangle used by the editor could be
// different from the one we proposed.
rect = editor->GetWindow()->GetRect();
// Ensure that the edit control fits into the visible part of the
// window by shifting it if necessary: we don't want to truncate
// any part of it by trying to position it too far to the left or
// top and we definitely don't want to start showing scrollbars by
// positioning it too far to the right or bottom.
const wxSize sizeMax = gridWindow->GetClientSize();
if ( !wxRect(sizeMax).Contains(rect) )
{
if ( rect.x < 0 )
rect.x = 0;
if ( rect.y < 0 )
rect.y = 0;
if ( rect.x > sizeMax.x - rect.width )
rect.x = sizeMax.x - rect.width;
if ( rect.y > sizeMax.y - rect.height )
rect.y = sizeMax.y - rect.height;
editor->GetWindow()->Move(rect.x, rect.y);
}
editor->Show( true, attr.get() );
// recalc dimensions in case we need to
// expand the scrolled window to account for editor
CalcDimensions();
editor->BeginEdit(row, col, this);
editor->SetCellAttr(NULL);
} }
} }
editor->SetCellAttr( attr.get() );
editor->SetSize( rect );
// Note that the actual rectangle used by the editor could be
// different from the one we proposed.
rect = editor->GetWindow()->GetRect();
// Ensure that the edit control fits into the visible part of the
// window by shifting it if necessary: we don't want to truncate
// any part of it by trying to position it too far to the left or
// top and we definitely don't want to start showing scrollbars by
// positioning it too far to the right or bottom.
const wxSize sizeMax = gridWindow->GetClientSize();
if ( !wxRect(sizeMax).Contains(rect) )
{
if ( rect.x < 0 )
rect.x = 0;
if ( rect.y < 0 )
rect.y = 0;
if ( rect.x > sizeMax.x - rect.width )
rect.x = sizeMax.x - rect.width;
if ( rect.y > sizeMax.y - rect.height )
rect.y = sizeMax.y - rect.height;
editor->GetWindow()->Move(rect.x, rect.y);
}
editor->Show( true, attr.get() );
// recalc dimensions in case we need to
// expand the scrolled window to account for editor
CalcDimensions();
editor->BeginEdit(row, col, this);
editor->SetCellAttr(NULL);
} }
void wxGrid::HideCellEditControl() void wxGrid::HideCellEditControl()
{ {
if ( IsCellEditControlEnabled() ) if ( IsCellEditControlEnabled() )
{ {
int row = m_currentCellCoords.GetRow(); DoHideCellEditControl();
int col = m_currentCellCoords.GetCol(); }
}
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col); void wxGrid::DoHideCellEditControl()
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col); {
const bool editorHadFocus = editor->GetWindow()->IsDescendant(FindFocus()); wxGridCellEditorPtr editor = GetCurrentCellEditorPtr();
const bool editorHadFocus = editor->GetWindow()->IsDescendant(FindFocus());
if ( editor->GetWindow()->GetParent() != m_gridWin ) if ( editor->GetWindow()->GetParent() != m_gridWin )
editor->GetWindow()->Reparent(m_gridWin); editor->GetWindow()->Reparent(m_gridWin);
editor->Show( false ); editor->Show( false );
wxGridWindow *gridWindow = CellToGridWindow(row, col); wxGridWindow *gridWindow = CellToGridWindow(m_currentCellCoords);
// return the focus to the grid itself if the editor had it // return the focus to the grid itself if the editor had it
// //
// note that we must not do this unconditionally to avoid stealing // note that we must not do this unconditionally to avoid stealing
// focus from the window which just received it if we are hiding the // focus from the window which just received it if we are hiding the
// editor precisely because we lost focus // editor precisely because we lost focus
if ( editorHadFocus ) if ( editorHadFocus )
gridWindow->SetFocus(); gridWindow->SetFocus();
// refresh whole row to the right // refresh whole row to the right
wxRect rect( CellToRect(row, col) ); wxRect rect( CellToRect(m_currentCellCoords) );
rect.Offset( -GetGridWindowOffset(gridWindow) ); rect.Offset( -GetGridWindowOffset(gridWindow) );
CalcGridWindowScrolledPosition(rect.x, rect.y, &rect.x, &rect.y, gridWindow); CalcGridWindowScrolledPosition(rect.x, rect.y, &rect.x, &rect.y, gridWindow);
rect.width = gridWindow->GetClientSize().GetWidth() - rect.x; rect.width = gridWindow->GetClientSize().GetWidth() - rect.x;
#ifdef __WXMAC__ #ifdef __WXMAC__
// ensure that the pixels under the focus ring get refreshed as well // ensure that the pixels under the focus ring get refreshed as well
rect.Inflate(10, 10); rect.Inflate(10, 10);
#endif #endif
gridWindow->Refresh( false, &rect ); gridWindow->Refresh( false, &rect );
// refresh also the grid to the right // refresh also the grid to the right
wxGridWindow *rightGridWindow = NULL; wxGridWindow *rightGridWindow = NULL;
if ( gridWindow->GetType() == wxGridWindow::wxGridWindowFrozenCorner ) if ( gridWindow->GetType() == wxGridWindow::wxGridWindowFrozenCorner )
rightGridWindow = m_frozenRowGridWin; rightGridWindow = m_frozenRowGridWin;
else if ( gridWindow->GetType() == wxGridWindow::wxGridWindowFrozenCol ) else if ( gridWindow->GetType() == wxGridWindow::wxGridWindowFrozenCol )
rightGridWindow = m_gridWin; rightGridWindow = m_gridWin;
if ( rightGridWindow ) if ( rightGridWindow )
{ {
rect.x = 0; rect.x = 0;
rect.width = rightGridWindow->GetClientSize().GetWidth(); rect.width = rightGridWindow->GetClientSize().GetWidth();
rightGridWindow->Refresh( false, &rect ); rightGridWindow->Refresh( false, &rect );
}
} }
} }
void wxGrid::AcceptCellEditControlIfShown()
{
if ( IsCellEditControlShown() )
{
DoAcceptCellEditControl();
}
}
void wxGrid::DoAcceptCellEditControl()
{
// Reset it first to avoid any problems with recursion via
// DisableCellEditControl() if it's called from the user-defined event
// handlers.
m_cellEditCtrlEnabled = false;
DoHideCellEditControl();
DoSaveEditControlValue();
}
void wxGrid::SaveEditControlValue() void wxGrid::SaveEditControlValue()
{ {
if ( IsCellEditControlEnabled() ) if ( IsCellEditControlEnabled() )
@@ -7450,10 +7438,9 @@ void wxGrid::DoSaveEditControlValue()
int row = m_currentCellCoords.GetRow(); int row = m_currentCellCoords.GetRow();
int col = m_currentCellCoords.GetCol(); int col = m_currentCellCoords.GetCol();
wxString oldval = GetCellValue(row, col); wxString oldval = GetCellValue(m_currentCellCoords);
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col); wxGridCellEditorPtr editor = GetCurrentCellEditorPtr();
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col);
wxString newval; wxString newval;
bool changed = editor->EndEdit(row, col, this, oldval, &newval); bool changed = editor->EndEdit(row, col, this, oldval, &newval);
@@ -7468,7 +7455,7 @@ void wxGrid::DoSaveEditControlValue()
if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 ) if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
{ {
// Event has been vetoed, set the data back. // Event has been vetoed, set the data back.
SetCellValue(row, col, oldval); SetCellValue(m_currentCellCoords, oldval);
} }
} }
} }
@@ -7652,13 +7639,12 @@ wxRect wxGrid::CellToRect( int row, int col ) const
{ {
int i, cell_rows, cell_cols; int i, cell_rows, cell_cols;
rect.width = rect.height = 0; rect.width = rect.height = 0;
GetCellSize( row, col, &cell_rows, &cell_cols ); if ( GetCellSize( row, col, &cell_rows, &cell_cols ) == CellSpan_Inside )
// if negative then find multicell owner {
if (cell_rows < 0)
row += cell_rows; row += cell_rows;
if (cell_cols < 0) col += cell_cols;
col += cell_cols; GetCellSize( row, col, &cell_rows, &cell_cols );
GetCellSize( row, col, &cell_rows, &cell_cols ); }
rect.x = GetColLeft(col); rect.x = GetColLeft(col);
rect.y = GetRowTop(row); rect.y = GetRowTop(row);
@@ -9970,9 +9956,7 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction)
wxClientDC dc(m_gridWin); wxClientDC dc(m_gridWin);
// cancel editing of cell AcceptCellEditControlIfShown();
HideCellEditControl();
SaveEditControlValue();
// initialize both of them just to avoid compiler warnings even if only // initialize both of them just to avoid compiler warnings even if only
// really needs to be initialized here // really needs to be initialized here
@@ -10291,11 +10275,7 @@ void wxGrid::AutoSizeRowLabelSize( int row )
{ {
// Hide the edit control, so it // Hide the edit control, so it
// won't interfere with drag-shrinking. // won't interfere with drag-shrinking.
if ( IsCellEditControlShown() ) AcceptCellEditControlIfShown();
{
HideCellEditControl();
SaveEditControlValue();
}
// autosize row height depending on label text // autosize row height depending on label text
SetRowSize(row, -1); SetRowSize(row, -1);
@@ -10307,11 +10287,7 @@ void wxGrid::AutoSizeColLabelSize( int col )
{ {
// Hide the edit control, so it // Hide the edit control, so it
// won't interfere with drag-shrinking. // won't interfere with drag-shrinking.
if ( IsCellEditControlShown() ) AcceptCellEditControlIfShown();
{
HideCellEditControl();
SaveEditControlValue();
}
// autosize column width depending on label text // autosize column width depending on label text
SetColSize(col, -1); SetColSize(col, -1);