+ if ( enable != m_cellEditCtrlEnabled )
+ {
+ if ( enable )
+ {
+ if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
+ return;
+
+ // this should be checked by the caller!
+ wxASSERT_MSG( CanEnableCellControl(), wxT("can't enable editing for this cell!") );
+
+ // do it before ShowCellEditControl()
+ m_cellEditCtrlEnabled = enable;
+
+ ShowCellEditControl();
+ }
+ else
+ {
+ SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
+
+ HideCellEditControl();
+ SaveEditControlValue();
+
+ // do it after HideCellEditControl()
+ m_cellEditCtrlEnabled = enable;
+ }
+ }
+}
+
+bool wxGrid::IsCurrentCellReadOnly() const
+{
+ // const_cast
+ wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords);
+ bool readonly = attr->IsReadOnly();
+ attr->DecRef();
+
+ return readonly;
+}
+
+bool wxGrid::CanEnableCellControl() const
+{
+ return m_editable && (m_currentCellCoords != wxGridNoCellCoords) &&
+ !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 isShown = false;
+
+ if ( m_cellEditCtrlEnabled )
+ {
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ wxGridCellEditor* editor = attr->GetEditor((wxGrid*) this, row, col);
+ attr->DecRef();
+
+ if ( editor )
+ {
+ if ( editor->IsCreated() )
+ {
+ isShown = editor->GetControl()->IsShown();
+ }
+
+ editor->DecRef();
+ }
+ }
+
+ return isShown;
+}
+
+void wxGrid::ShowCellEditControl()
+{
+ if ( IsCellEditControlEnabled() )
+ {
+ if ( !IsVisible( m_currentCellCoords, false ) )
+ {
+ m_cellEditCtrlEnabled = false;
+ return;
+ }
+ else
+ {
+ wxRect rect = CellToRect( m_currentCellCoords );
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+
+ // if this is part of a multicell, find owner (topleft)
+ int cell_rows, cell_cols;
+ GetCellSize( row, col, &cell_rows, &cell_cols );
+ if ( cell_rows <= 0 || cell_cols <= 0 )
+ {
+ row += cell_rows;
+ col += cell_cols;
+ m_currentCellCoords.SetRow( row );
+ m_currentCellCoords.SetCol( col );
+ }
+
+ // erase the highlight and the cell contents because the editor
+ // might not cover the entire cell
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ dc.SetBrush(wxBrush(attr->GetBackgroundColour()));
+ dc.SetPen(*wxTRANSPARENT_PEN);
+ dc.DrawRectangle(rect);
+
+ // convert to scrolled coords
+ CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+ int nXMove = 0;
+ if (rect.x < 0)
+ nXMove = rect.x;
+
+ // cell is shifted by one pixel
+ // However, don't allow x or y to become negative
+ // since the SetSize() method interprets that as
+ // "don't change."
+ if (rect.x > 0)
+ rect.x--;
+ if (rect.y > 0)
+ rect.y--;
+
+ wxGridCellEditor* editor = attr->GetEditor(this, row, col);
+ if ( !editor->IsCreated() )
+ {
+ editor->Create(m_gridWin, wxID_ANY,
+ new wxGridCellEditorEvtHandler(this, editor));
+
+ wxGridEditorCreatedEvent evt(GetId(),
+ wxEVT_GRID_EDITOR_CREATED,
+ this,
+ row,
+ col,
+ editor->GetControl());
+ GetEventHandler()->ProcessEvent(evt);
+ }
+
+ // resize editor to overflow into righthand cells if allowed
+ int maxWidth = rect.width;
+ wxString value = GetCellValue(row, col);
+ if ( (value != wxEmptyString) && (attr->GetOverflow()) )
+ {
+ int y;
+ GetTextExtent(value, &maxWidth, &y, NULL, NULL, &attr->GetFont());
+ if (maxWidth < rect.width)
+ maxWidth = rect.width;
+ }
+
+ int client_right = m_gridWin->GetClientSize().GetWidth();
+ if (rect.x + maxWidth > client_right)
+ maxWidth = client_right - rect.x;
+
+ 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;
+ }
+
+ if (rect.GetRight() > client_right)
+ rect.SetRight( client_right - 1 );
+ }
+
+ editor->SetCellAttr( attr );
+ editor->SetSize( rect );
+ if (nXMove != 0)
+ editor->GetControl()->Move(
+ editor->GetControl()->GetPosition().x + nXMove,
+ editor->GetControl()->GetPosition().y );
+ editor->Show( true, attr );
+
+ // 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->DecRef();
+ attr->DecRef();
+ }
+ }
+}
+
+void wxGrid::HideCellEditControl()
+{
+ if ( IsCellEditControlEnabled() )
+ {
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+
+ wxGridCellAttr *attr = GetCellAttr(row, col);
+ wxGridCellEditor *editor = attr->GetEditor(this, row, col);
+ const bool editorHadFocus = editor->GetControl()->HasFocus();
+ editor->Show( false );
+ editor->DecRef();
+ attr->DecRef();
+
+ // return the focus to the grid itself if the editor had it
+ //
+ // note that we must not do this unconditionally to avoid stealing
+ // focus from the window which just received it if we are hiding the
+ // editor precisely because we lost focus
+ if ( editorHadFocus )
+ m_gridWin->SetFocus();
+
+ // refresh whole row to the right
+ wxRect rect( CellToRect(row, col) );
+ CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y );
+ rect.width = m_gridWin->GetClientSize().GetWidth() - rect.x;
+
+#ifdef __WXMAC__
+ // ensure that the pixels under the focus ring get refreshed as well
+ rect.Inflate(10, 10);
+#endif
+
+ m_gridWin->Refresh( false, &rect );
+ }
+}
+
+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();
+ }
+}
+
+//
+// ------ Grid location functions
+// Note that all of these functions work with the logical coordinates of
+// grid cells and labels so you will need to convert from device
+// coordinates for mouse events etc.
+//
+
+wxGridCellCoords wxGrid::XYToCell(int x, int y) const
+{
+ int row = YToRow(y);
+ int col = XToCol(x);
+
+ return row == -1 || col == -1 ? wxGridNoCellCoords
+ : wxGridCellCoords(row, col);
+}
+
+// compute row or column from some (unscrolled) coordinate value, using either
+// m_defaultRowHeight/m_defaultColWidth or binary search on array of
+// m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used
+// for large grids)
+int wxGrid::PosToLinePos(int coord,
+ bool clipToMinMax,
+ const wxGridOperations& oper) const
+{
+ const int numLines = oper.GetNumberOfLines(this);
+
+ if ( coord < 0 )
+ return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND;
+
+ const int defaultLineSize = oper.GetDefaultLineSize(this);
+ wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" );
+
+ int maxPos = coord / defaultLineSize,
+ minPos = 0;
+
+ // check for the simplest case: if we have no explicit line sizes
+ // configured, then we already know the line this position falls in
+ const wxArrayInt& lineEnds = oper.GetLineEnds(this);
+ if ( lineEnds.empty() )
+ {
+ if ( maxPos < numLines )
+ return maxPos;
+
+ return clipToMinMax ? numLines - 1 : -1;
+ }
+
+
+ // adjust maxPos before starting the binary search
+ if ( maxPos >= numLines )
+ {
+ maxPos = numLines - 1;
+ }
+ else
+ {
+ if ( coord >= lineEnds[oper.GetLineAt(this, maxPos)])
+ {
+ minPos = maxPos;
+ const int minDist = oper.GetMinimalAcceptableLineSize(this);
+ if ( minDist )
+ maxPos = coord / minDist;
+ else
+ maxPos = numLines - 1;
+ }
+
+ if ( maxPos >= numLines )
+ maxPos = numLines - 1;
+ }
+
+ // check if the position is beyond the last column
+ const int lineAtMaxPos = oper.GetLineAt(this, maxPos);
+ if ( coord >= lineEnds[lineAtMaxPos] )
+ return clipToMinMax ? maxPos : -1;
+
+ // or before the first one
+ const int lineAt0 = oper.GetLineAt(this, 0);
+ if ( coord < lineEnds[lineAt0] )
+ return 0;
+
+
+ // finally do perform the binary search
+ while ( minPos < maxPos )
+ {
+ wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord &&
+ coord < lineEnds[oper.GetLineAt(this, maxPos)],
+ -1,
+ "wxGrid: internal error in PosToLinePos()" );
+
+ if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] )
+ return maxPos;
+ else
+ maxPos--;
+
+ const int median = minPos + (maxPos - minPos + 1) / 2;
+ if ( coord < lineEnds[oper.GetLineAt(this, median)] )
+ maxPos = median;
+ else
+ minPos = median;
+ }
+
+ return maxPos;
+}
+
+int
+wxGrid::PosToLine(int coord,
+ bool clipToMinMax,
+ const wxGridOperations& oper) const
+{
+ int pos = PosToLinePos(coord, clipToMinMax, oper);
+
+ return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos);
+}
+
+int wxGrid::YToRow(int y, bool clipToMinMax) const
+{
+ return PosToLine(y, clipToMinMax, wxGridRowOperations());
+}
+
+int wxGrid::XToCol(int x, bool clipToMinMax) const
+{
+ return PosToLine(x, clipToMinMax, wxGridColumnOperations());
+}
+
+int wxGrid::XToPos(int x) const
+{
+ return PosToLinePos(x, true /* clip */, wxGridColumnOperations());
+}
+
+// return the row number such that the y coord is near the edge of, or -1 if
+// not near an edge.
+//
+// notice that position can only possibly be near an edge if the row/column is
+// large enough to still allow for an "inner" area that is _not_ near the edge
+// (i.e., if the height/width is smaller than WXGRID_LABEL_EDGE_ZONE, pos will
+// _never_ be considered to be near the edge).
+int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const
+{
+ const int line = oper.PosToLine(this, pos, true);
+
+ if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE )
+ {
+ // We know that we are in this line, test whether we are close enough
+ // to start or end border, respectively.
+ if ( abs(oper.GetLineEndPos(this, line) - pos) < WXGRID_LABEL_EDGE_ZONE )
+ return line;
+ else if ( line > 0 &&
+ pos - oper.GetLineStartPos(this,
+ line) < WXGRID_LABEL_EDGE_ZONE )
+ return line - 1;
+ }
+
+ return -1;
+}
+
+int wxGrid::YToEdgeOfRow(int y) const
+{
+ return PosToEdgeOfLine(y, wxGridRowOperations());
+}
+
+int wxGrid::XToEdgeOfCol(int x) const
+{
+ return PosToEdgeOfLine(x, wxGridColumnOperations());
+}
+
+wxRect wxGrid::CellToRect( int row, int col ) const
+{
+ wxRect rect( -1, -1, -1, -1 );
+
+ if ( row >= 0 && row < m_numRows &&
+ col >= 0 && col < m_numCols )
+ {
+ int i, cell_rows, cell_cols;
+ rect.width = rect.height = 0;
+ GetCellSize( row, col, &cell_rows, &cell_cols );
+ // if negative then find multicell owner
+ if (cell_rows < 0)
+ row += cell_rows;
+ if (cell_cols < 0)
+ col += cell_cols;
+ GetCellSize( row, col, &cell_rows, &cell_cols );
+
+ rect.x = GetColLeft(col);
+ rect.y = GetRowTop(row);
+ for (i=col; i < col + cell_cols; i++)
+ rect.width += GetColWidth(i);
+ for (i=row; i < row + cell_rows; i++)
+ rect.height += GetRowHeight(i);
+
+ // if grid lines are enabled, then the area of the cell is a bit smaller
+ if (m_gridLinesEnabled)
+ {
+ rect.width -= 1;
+ rect.height -= 1;
+ }
+ }
+
+ return rect;
+}
+
+bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible ) const
+{
+ // get the cell rectangle in logical coords
+ //
+ wxRect r( CellToRect( row, col ) );
+
+ // convert to device coords
+ //
+ int left, top, right, bottom;