X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ff72f628bbcbe3a8199aa15f79389f830ddbbb8a..e342075f460fded9f5dc2f8b186eb9de5ab72f3e:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index e9c3813f83..41fe1d6fc9 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -9,6 +9,15 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// +/* + TODO: + + - Replace use of wxINVERT with wxOverlay + - Make Begin/EndBatch() the same as the generic Freeze/Thaw() + - Review the column reordering code, it's a mess. + - Implement row reordering after dealing with the columns. + */ + // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" @@ -388,7 +397,7 @@ WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridDataTypeInfo*, wxGridDataTypeInfoArray, class WXDLLIMPEXP_ADV wxGridTypeRegistry { public: - wxGridTypeRegistry() {} + wxGridTypeRegistry() {} ~wxGridTypeRegistry(); void RegisterDataType(const wxString& typeName, @@ -414,6 +423,351 @@ private: wxGridDataTypeInfoArray m_typeinfo; }; +// ---------------------------------------------------------------------------- +// operations classes abstracting the difference between operating on rows and +// columns +// ---------------------------------------------------------------------------- + +// This class allows to write a function only once because by using its methods +// it will apply to both columns and rows. +// +// This is an abstract interface definition, the two concrete implementations +// below should be used when working with rows and columns respectively. +class wxGridOperations +{ +public: + // Returns the operations in the other direction, i.e. wxGridRowOperations + // if this object is a wxGridColumnOperations and vice versa. + virtual wxGridOperations& Dual() const = 0; + + // Return the number of rows or columns. + virtual int GetNumberOfLines(const wxGrid *grid) const = 0; + + // Return the selection mode which allows selecting rows or columns. + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const = 0; + + // Make a wxGridCellCoords from the given components: thisDir is row or + // column and otherDir is column or row + virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const = 0; + + // Calculate the scrolled position of the given abscissa or ordinate. + virtual int CalcScrolledPosition(wxGrid *grid, int pos) const = 0; + + // Selects the horizontal or vertical component from the given object. + virtual int Select(const wxGridCellCoords& coords) const = 0; + virtual int Select(const wxPoint& pt) const = 0; + virtual int Select(const wxSize& sz) const = 0; + virtual int Select(const wxRect& r) const = 0; + virtual int& Select(wxRect& r) const = 0; + + // Returns width or height of the rectangle + virtual int& SelectSize(wxRect& r) const = 0; + + // Make a wxSize such that Select() applied to it returns first component + virtual wxSize MakeSize(int first, int second) const = 0; + + // Sets the row or column component of the given cell coordinates + virtual void Set(wxGridCellCoords& coords, int line) const = 0; + + + // Draws a line parallel to the row or column, i.e. horizontal or vertical: + // pos is the vertical or horizontal position of the line and start and end + // are the coordinates of the line extremities in the other direction + virtual void + DrawParallelLine(wxDC& dc, int start, int end, int pos) const = 0; + + + // Return the row or column at the given pixel coordinate. + virtual int + PosToLine(const wxGrid *grid, int pos, bool clip = false) const = 0; + + // Get the top/left position, in pixels, of the given row or column + virtual int GetLineStartPos(const wxGrid *grid, int line) const = 0; + + // Get the bottom/right position, in pixels, of the given row or column + virtual int GetLineEndPos(const wxGrid *grid, int line) const = 0; + + // Get the height/width of the given row/column + virtual int GetLineSize(const wxGrid *grid, int line) const = 0; + + // Get wxGrid::m_rowBottoms/m_colRights array + virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const = 0; + + // Get default height row height or column width + virtual int GetDefaultLineSize(const wxGrid *grid) const = 0; + + // Return the minimal acceptable row height or column width + virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const = 0; + + // Return the minimal row height or column width + virtual int GetMinimalLineSize(const wxGrid *grid, int line) const = 0; + + // Set the row height or column width + virtual void SetLineSize(wxGrid *grid, int line, int size) const = 0; + + // True if rows/columns can be resized by user + virtual bool CanResizeLines(const wxGrid *grid) const = 0; + + + // Return the index of the line at the given position + // + // NB: currently this is always identity for the rows as reordering is only + // implemented for the lines + virtual int GetLineAt(const wxGrid *grid, int line) const = 0; + + + // Get the row or column label window + virtual wxWindow *GetHeaderWindow(wxGrid *grid) const = 0; + + // Get the width or height of the row or column label window + virtual int GetHeaderWindowSize(wxGrid *grid) const = 0; + + + // This class is never used polymorphically but give it a virtual dtor + // anyhow to suppress g++ complaints about it + virtual ~wxGridOperations() { } +}; + +class wxGridRowOperations : public wxGridOperations +{ +public: + virtual wxGridOperations& Dual() const; + + virtual int GetNumberOfLines(const wxGrid *grid) const + { return grid->GetNumberRows(); } + + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const + { return wxGrid::wxGridSelectRows; } + + virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const + { return wxGridCellCoords(thisDir, otherDir); } + + virtual int CalcScrolledPosition(wxGrid *grid, int pos) const + { return grid->CalcScrolledPosition(wxPoint(pos, 0)).x; } + + virtual int Select(const wxGridCellCoords& c) const { return c.GetRow(); } + virtual int Select(const wxPoint& pt) const { return pt.x; } + virtual int Select(const wxSize& sz) const { return sz.x; } + virtual int Select(const wxRect& r) const { return r.x; } + virtual int& Select(wxRect& r) const { return r.x; } + virtual int& SelectSize(wxRect& r) const { return r.width; } + virtual wxSize MakeSize(int first, int second) const + { return wxSize(first, second); } + virtual void Set(wxGridCellCoords& coords, int line) const + { coords.SetRow(line); } + + virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const + { dc.DrawLine(start, pos, end, pos); } + + virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const + { return grid->YToRow(pos, clip); } + virtual int GetLineStartPos(const wxGrid *grid, int line) const + { return grid->GetRowTop(line); } + virtual int GetLineEndPos(const wxGrid *grid, int line) const + { return grid->GetRowBottom(line); } + virtual int GetLineSize(const wxGrid *grid, int line) const + { return grid->GetRowHeight(line); } + virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const + { return grid->m_rowBottoms; } + virtual int GetDefaultLineSize(const wxGrid *grid) const + { return grid->GetDefaultRowSize(); } + virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const + { return grid->GetRowMinimalAcceptableHeight(); } + virtual int GetMinimalLineSize(const wxGrid *grid, int line) const + { return grid->GetRowMinimalHeight(line); } + virtual void SetLineSize(wxGrid *grid, int line, int size) const + { grid->SetRowSize(line, size); } + virtual bool CanResizeLines(const wxGrid *grid) const + { return grid->CanDragRowSize(); } + + virtual int GetLineAt(const wxGrid * WXUNUSED(grid), int line) const + { return line; } // TODO: implement row reordering + + virtual wxWindow *GetHeaderWindow(wxGrid *grid) const + { return grid->GetGridRowLabelWindow(); } + virtual int GetHeaderWindowSize(wxGrid *grid) const + { return grid->GetRowLabelSize(); } +}; + +class wxGridColumnOperations : public wxGridOperations +{ +public: + virtual wxGridOperations& Dual() const; + + virtual int GetNumberOfLines(const wxGrid *grid) const + { return grid->GetNumberCols(); } + + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const + { return wxGrid::wxGridSelectColumns; } + + virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const + { return wxGridCellCoords(otherDir, thisDir); } + + virtual int CalcScrolledPosition(wxGrid *grid, int pos) const + { return grid->CalcScrolledPosition(wxPoint(0, pos)).y; } + + virtual int Select(const wxGridCellCoords& c) const { return c.GetCol(); } + virtual int Select(const wxPoint& pt) const { return pt.y; } + virtual int Select(const wxSize& sz) const { return sz.y; } + virtual int Select(const wxRect& r) const { return r.y; } + virtual int& Select(wxRect& r) const { return r.y; } + virtual int& SelectSize(wxRect& r) const { return r.height; } + virtual wxSize MakeSize(int first, int second) const + { return wxSize(second, first); } + virtual void Set(wxGridCellCoords& coords, int line) const + { coords.SetCol(line); } + + virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const + { dc.DrawLine(pos, start, pos, end); } + + virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const + { return grid->XToCol(pos, clip); } + virtual int GetLineStartPos(const wxGrid *grid, int line) const + { return grid->GetColLeft(line); } + virtual int GetLineEndPos(const wxGrid *grid, int line) const + { return grid->GetColRight(line); } + virtual int GetLineSize(const wxGrid *grid, int line) const + { return grid->GetColWidth(line); } + virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const + { return grid->m_colRights; } + virtual int GetDefaultLineSize(const wxGrid *grid) const + { return grid->GetDefaultColSize(); } + virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const + { return grid->GetColMinimalAcceptableWidth(); } + virtual int GetMinimalLineSize(const wxGrid *grid, int line) const + { return grid->GetColMinimalWidth(line); } + virtual void SetLineSize(wxGrid *grid, int line, int size) const + { grid->SetColSize(line, size); } + virtual bool CanResizeLines(const wxGrid *grid) const + { return grid->CanDragColSize(); } + + virtual int GetLineAt(const wxGrid *grid, int line) const + { return grid->GetColAt(line); } + + virtual wxWindow *GetHeaderWindow(wxGrid *grid) const + { return grid->GetGridColLabelWindow(); } + virtual int GetHeaderWindowSize(wxGrid *grid) const + { return grid->GetColLabelSize(); } +}; + +wxGridOperations& wxGridRowOperations::Dual() const +{ + static wxGridColumnOperations s_colOper; + + return s_colOper; +} + +wxGridOperations& wxGridColumnOperations::Dual() const +{ + static wxGridRowOperations s_rowOper; + + return s_rowOper; +} + +// This class abstracts the difference between operations going forward +// (down/right) and backward (up/left) and allows to use the same code for +// functions which differ only in the direction of grid traversal +// +// Like wxGridOperations it's an ABC with two concrete subclasses below. Unlike +// it, this is a normal object and not just a function dispatch table and has a +// non-default ctor. +// +// Note: the explanation of this discrepancy is the existence of (very useful) +// Dual() method in wxGridOperations which forces us to make wxGridOperations a +// function dispatcher only. +class wxGridDirectionOperations +{ +public: + // The oper parameter to ctor selects whether we work with rows or columns + wxGridDirectionOperations(wxGrid *grid, const wxGridOperations& oper) + : m_grid(grid), + m_oper(oper) + { + } + + // Check if the component of this point in our direction is at the + // boundary, i.e. is the first/last row/column + virtual bool IsAtBoundary(const wxGridCellCoords& coords) const = 0; + + // Increment the component of this point in our direction + virtual void Advance(wxGridCellCoords& coords) const = 0; + + // Find the line at the given distance, in pixels, away from this one + // (this uses clipping, i.e. anything after the last line is counted as the + // last one and anything before the first one as 0) + virtual int MoveByPixelDistance(int line, int distance) const = 0; + + // This class is never used polymorphically but give it a virtual dtor + // anyhow to suppress g++ complaints about it + virtual ~wxGridDirectionOperations() { } + +protected: + wxGrid * const m_grid; + const wxGridOperations& m_oper; +}; + +class wxGridBackwardOperations : public wxGridDirectionOperations +{ +public: + wxGridBackwardOperations(wxGrid *grid, const wxGridOperations& oper) + : wxGridDirectionOperations(grid, oper) + { + } + + virtual bool IsAtBoundary(const wxGridCellCoords& coords) const + { + wxASSERT_MSG( m_oper.Select(coords) >= 0, "invalid row/column" ); + + return m_oper.Select(coords) == 0; + } + + virtual void Advance(wxGridCellCoords& coords) const + { + wxASSERT( !IsAtBoundary(coords) ); + + m_oper.Set(coords, m_oper.Select(coords) - 1); + } + + virtual int MoveByPixelDistance(int line, int distance) const + { + int pos = m_oper.GetLineStartPos(m_grid, line); + return m_oper.PosToLine(m_grid, pos - distance + 1, true); + } +}; + +class wxGridForwardOperations : public wxGridDirectionOperations +{ +public: + wxGridForwardOperations(wxGrid *grid, const wxGridOperations& oper) + : wxGridDirectionOperations(grid, oper), + m_numLines(oper.GetNumberOfLines(grid)) + { + } + + virtual bool IsAtBoundary(const wxGridCellCoords& coords) const + { + wxASSERT_MSG( m_oper.Select(coords) < m_numLines, "invalid row/column" ); + + return m_oper.Select(coords) == m_numLines - 1; + } + + virtual void Advance(wxGridCellCoords& coords) const + { + wxASSERT( !IsAtBoundary(coords) ); + + m_oper.Set(coords, m_oper.Select(coords) + 1); + } + + virtual int MoveByPixelDistance(int line, int distance) const + { + int pos = m_oper.GetLineStartPos(m_grid, line); + return m_oper.PosToLine(m_grid, pos + distance, true); + } + +private: + const int m_numLines; +}; + // ---------------------------------------------------------------------------- // globals // ---------------------------------------------------------------------------- @@ -439,6 +793,23 @@ static const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; // in these hash tables is the number of rows/columns) static const int GRID_HASH_SIZE = 100; +// ---------------------------------------------------------------------------- +// private helpers +// ---------------------------------------------------------------------------- + +namespace +{ + +// ensure that first is less or equal to second, swapping the values if +// necessary +void EnsureFirstLessThanSecond(int& first, int& second) +{ + if ( first > second ) + wxSwap(first, second); +} + +} // anonymous namespace + // ============================================================================ // implementation // ============================================================================ @@ -2634,7 +3005,7 @@ void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col) wxGridCellAttr *wxGridCellAttrData::GetAttr(int row, int col) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; int n = FindIndex(row, col); if ( n != wxNOT_FOUND ) @@ -2744,7 +3115,7 @@ wxGridRowOrColAttrData::~wxGridRowOrColAttrData() wxGridCellAttr *wxGridRowOrColAttrData::GetAttr(int rowOrCol) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; int n = m_rowsOrCols.Index(rowOrCol); if ( n != wxNOT_FOUND ) @@ -2830,7 +3201,7 @@ void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols wxGridCellAttrProvider::wxGridCellAttrProvider() { - m_data = (wxGridCellAttrProviderData *)NULL; + m_data = NULL; } wxGridCellAttrProvider::~wxGridCellAttrProvider() @@ -2846,7 +3217,7 @@ void wxGridCellAttrProvider::InitData() wxGridCellAttr *wxGridCellAttrProvider::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind ) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; if ( m_data ) { switch (kind) @@ -3148,8 +3519,8 @@ IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject ) wxGridTableBase::wxGridTableBase() { - m_view = (wxGrid *) NULL; - m_attrProvider = (wxGridCellAttrProvider *) NULL; + m_view = NULL; + m_attrProvider = NULL; } wxGridTableBase::~wxGridTableBase() @@ -3179,7 +3550,7 @@ wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col, wxGridCellAttr::wxAtt if ( m_attrProvider ) return m_attrProvider->GetAttr(row, col, kind); else - return (wxGridCellAttr *)NULL; + return NULL; } void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col) @@ -3378,7 +3749,7 @@ void wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col), wxGridTableMessage::wxGridTableMessage() { - m_table = (wxGridTableBase *) NULL; + m_table = NULL; m_id = -1; m_comInt1 = -1; m_comInt2 = -1; @@ -4040,23 +4411,8 @@ void wxGridWindow::OnFocus(wxFocusEvent& event) event.Skip(); } -////////////////////////////////////////////////////////////////////// - -// Internal Helper function for computing 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 speed up the search! - -// Internal helper macros for simpler use of that function - -static int CoordToRowOrCol(int coord, int defaultDist, int minDist, - const wxArrayInt& BorderArray, int nMax, - bool clipToMinMax); - #define internalXToCol(x) XToCol(x, true) -#define internalYToRow(y) CoordToRowOrCol(y, m_defaultRowHeight, \ - m_minAcceptableRowHeight, \ - m_rowBottoms, m_numRows, true) +#define internalYToRow(y) YToRow(y, true) ///////////////////////////////////////////////////////////////////// @@ -4279,28 +4635,16 @@ void wxGrid::Create() } bool wxGrid::CreateGrid( int numRows, int numCols, - wxGrid::wxGridSelectionModes selmode ) + wxGridSelectionModes selmode ) { wxCHECK_MSG( !m_created, false, wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") ); - m_numRows = numRows; - m_numCols = numCols; - - m_table = new wxGridStringTable( m_numRows, m_numCols ); - m_table->SetView( this ); - m_ownTable = true; - m_selection = new wxGridSelection( this, selmode ); - - CalcDimensions(); - - m_created = true; - - return m_created; + return SetTable(new wxGridStringTable(numRows, numCols), true, selmode); } -void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode) +void wxGrid::SetSelectionMode(wxGridSelectionModes selmode) { wxCHECK_RET( m_created, wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") ); @@ -4310,14 +4654,16 @@ void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode) wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const { - wxCHECK_MSG( m_created, wxGrid::wxGridSelectCells, + wxCHECK_MSG( m_created, wxGridSelectCells, wxT("Called wxGrid::GetSelectionMode() before calling CreateGrid()") ); return m_selection->GetSelectionMode(); } -bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, - wxGrid::wxGridSelectionModes selmode ) +bool +wxGrid::SetTable(wxGridTableBase *table, + bool takeOwnership, + wxGrid::wxGridSelectionModes selmode ) { bool checkSelection = false; if ( m_created ) @@ -4457,7 +4803,7 @@ void wxGrid::Init() m_canDragColMove = false; m_cursorMode = WXGRID_CURSOR_SELECT_CELL; - m_winCapture = (wxWindow *)NULL; + m_winCapture = NULL; m_canDragRowSize = true; m_canDragColSize = true; m_canDragGridSize = true; @@ -5833,7 +6179,7 @@ void wxGrid::ChangeCursorMode(CursorMode mode, { if (m_winCapture->HasCapture()) m_winCapture->ReleaseMouse(); - m_winCapture = (wxWindow *)NULL; + m_winCapture = NULL; } m_cursorMode = mode; @@ -6251,124 +6597,106 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) } } -void wxGrid::DoEndDragResizeRow() +void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) { - if ( m_dragLastPos >= 0 ) - { - // erase the last line and resize the row - // - int cw, ch, left, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &left, &dummy ); + if ( m_dragLastPos == -1 ) + return; - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - dc.SetLogicalFunction( wxINVERT ); - dc.DrawLine( left, m_dragLastPos, left + cw, m_dragLastPos ); - HideCellEditControl(); - SaveEditControlValue(); + const wxGridOperations& doper = oper.Dual(); - int rowTop = GetRowTop(m_dragRowOrCol); - SetRowSize( m_dragRowOrCol, - wxMax( m_dragLastPos - rowTop, m_minAcceptableRowHeight ) ); + const wxSize size = m_gridWin->GetClientSize(); - if ( !GetBatchCount() ) - { - // Only needed to get the correct rect.y: - wxRect rect ( CellToRect( m_dragRowOrCol, 0 ) ); - rect.x = 0; - CalcScrolledPosition(0, rect.y, &dummy, &rect.y); - rect.width = m_rowLabelWidth; - rect.height = ch - rect.y; - m_rowLabelWin->Refresh( true, &rect ); - rect.width = cw; + const wxPoint ptOrigin = CalcUnscrolledPosition(wxPoint(0, 0)); - // if there is a multicell block, paint all of it - if (m_table) - { - int i, cell_rows, cell_cols, subtract_rows = 0; - int leftCol = XToCol(left); - int rightCol = internalXToCol(left + cw); - if (leftCol >= 0) - { - for (i=leftCol; iRefresh( false, &rect ); - } + // erase the last line we drew + wxClientDC dc(m_gridWin); + PrepareDC(dc); + dc.SetLogicalFunction(wxINVERT); - ShowCellEditControl(); - } -} + const int posLineStart = oper.Select(ptOrigin); + const int posLineEnd = oper.Select(ptOrigin) + oper.Select(size); + oper.DrawParallelLine(dc, posLineStart, posLineEnd, m_dragLastPos); -void wxGrid::DoEndDragResizeCol() -{ - if ( m_dragLastPos >= 0 ) + // temporarily hide the edit control before resizing + HideCellEditControl(); + SaveEditControlValue(); + + // do resize the line + const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol); + oper.SetLineSize(this, m_dragRowOrCol, + wxMax(m_dragLastPos - lineStart, + oper.GetMinimalLineSize(this, m_dragRowOrCol))); + + // refresh now if we're not frozen + if ( !GetBatchCount() ) { - // erase the last line and resize the col - // - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); + // we need to refresh everything beyond the resized line in the header + // window - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - dc.SetLogicalFunction( wxINVERT ); - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - HideCellEditControl(); - SaveEditControlValue(); + // get the position from which to refresh in the other direction + wxRect rect(CellToRect(oper.MakeCoords(m_dragRowOrCol, 0))); + rect.SetPosition(CalcScrolledPosition(rect.GetPosition())); - int colLeft = GetColLeft(m_dragRowOrCol); - SetColSize( m_dragRowOrCol, - wxMax( m_dragLastPos - colLeft, - GetColMinimalWidth(m_dragRowOrCol) ) ); + // we only need the ordinate (for rows) or abscissa (for columns) here, + // and need to cover the entire window in the other direction + oper.Select(rect) = 0; - if ( !GetBatchCount() ) + wxRect rectHeader(rect.GetPosition(), + oper.MakeSize + ( + oper.GetHeaderWindowSize(this), + doper.Select(size) - doper.Select(rect) + )); + + oper.GetHeaderWindow(this)->Refresh(true, &rectHeader); + + + // also refresh the grid window: extend the rectangle + if ( m_table ) { - // Only needed to get the correct rect.x: - wxRect rect ( CellToRect( 0, m_dragRowOrCol ) ); - rect.y = 0; - CalcScrolledPosition(rect.x, 0, &rect.x, &dummy); - rect.width = cw - rect.x; - rect.height = m_colLabelHeight; - m_colLabelWin->Refresh( true, &rect ); - rect.height = ch; + oper.SelectSize(rect) = oper.Select(size); - // if there is a multicell block, paint all of it - if (m_table) + int subtractLines = 0; + const int lineStart = oper.PosToLine(this, posLineStart); + if ( lineStart >= 0 ) { - int i, cell_rows, cell_cols, subtract_cols = 0; - int topRow = YToRow(top); - int bottomRow = internalYToRow(top + cw); - if (topRow >= 0) + // ensure that if we have a multi-cell block we redraw all of + // it by increasing the refresh area to cover it entirely if a + // part of it is affected + const int lineEnd = oper.PosToLine(this, posLineEnd, true); + for ( int line = lineStart; line < lineEnd; line++ ) { - for (i=topRow; iRefresh( false, &rect ); - } + int startPos = + oper.GetLineStartPos(this, m_dragRowOrCol + subtractLines); + startPos = doper.CalcScrolledPosition(this, startPos); + + doper.Select(rect) = startPos; + doper.SelectSize(rect) = doper.Select(size) - startPos; - ShowCellEditControl(); + m_gridWin->Refresh(false, &rect); + } } + + // show the edit control back again + ShowCellEditControl(); +} + +void wxGrid::DoEndDragResizeRow() +{ + DoEndDragResizeLine(wxGridRowOperations()); +} + +void wxGrid::DoEndDragResizeCol() +{ + DoEndDragResizeLine(wxGridColumnOperations()); } void wxGrid::DoEndDragMoveCol() @@ -6505,7 +6833,7 @@ bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg ) // The behaviour of this function depends on the grid table class // Clear() function. For the default wxGridStringTable class the -// behavious is to replace all cell contents with wxEmptyString but +// behaviour is to replace all cell contents with wxEmptyString but // not to change the number of rows or cols. // void wxGrid::ClearGrid() @@ -6521,150 +6849,52 @@ void wxGrid::ClearGrid() } } -bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t), + int pos, int num, bool WXUNUSED(updateLabels) ) { - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->InsertRows( pos, numRows ); - return done; - - // the table will have sent the results of the insert row - // operation to this view object as a grid table message - } - - return false; -} + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); -bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) ) -{ - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") ); + if ( !m_table ) return false; - } - if ( m_table ) - { - bool done = m_table && m_table->AppendRows( numRows ); - return done; + if ( IsCellEditControlEnabled() ) + DisableCellEditControl(); - // the table will have sent the results of the append row - // operation to this view object as a grid table message - } + return (m_table->*funcModify)(pos, num); - return false; + // the table will have sent the results of the insert row + // operation to this view object as a grid table message } -bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t), + int num, bool WXUNUSED(updateLabels)) { - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); - bool done = m_table->DeleteRows( pos, numRows ); - return done; - // the table will have sent the results of the delete row - // operation to this view object as a grid table message - } + if ( !m_table ) + return false; - return false; + return (m_table->*funcAppend)(num); } -bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) +// +// ----- event handlers +// + +// Generate a grid event based on a mouse event and +// return the result of ProcessEvent() +// +int wxGrid::SendEvent( const wxEventType type, + int row, int col, + wxMouseEvent& mouseEv ) { - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") ); - return false; - } + bool claimed, vetoed; - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->InsertCols( pos, numCols ); - return done; - // the table will have sent the results of the insert col - // operation to this view object as a grid table message - } - - return false; -} - -bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) ) -{ - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - bool done = m_table->AppendCols( numCols ); - return done; - // the table will have sent the results of the append col - // operation to this view object as a grid table message - } - - return false; -} - -bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) -{ - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->DeleteCols( pos, numCols ); - return done; - // the table will have sent the results of the delete col - // operation to this view object as a grid table message - } - - return false; -} - -// -// ----- event handlers -// - -// Generate a grid event based on a mouse event and -// return the result of ProcessEvent() -// -int wxGrid::SendEvent( const wxEventType type, - int row, int col, - wxMouseEvent& mouseEv ) -{ - bool claimed, vetoed; - - if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE ) - { - int rowOrCol = (row == -1 ? col : row); + if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE ) + { + int rowOrCol = (row == -1 ? col : row); wxGridSizeEvent gridEvt( GetId(), type, @@ -7189,9 +7419,8 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) attr->DecRef(); } -void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCol ) +void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol) { - int temp; wxGridCellCoords updateTopLeft, updateBottomRight; if ( m_selection ) @@ -7208,19 +7437,8 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo } } - if ( topRow > bottomRow ) - { - temp = topRow; - topRow = bottomRow; - bottomRow = temp; - } - - if ( leftCol > rightCol ) - { - temp = leftCol; - leftCol = rightCol; - rightCol = temp; - } + EnsureFirstLessThanSecond(topRow, bottomRow); + EnsureFirstLessThanSecond(leftCol, rightCol); updateTopLeft = wxGridCellCoords( topRow, leftCol ); updateBottomRight = wxGridCellCoords( bottomRow, rightCol ); @@ -7257,30 +7475,10 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo wxCoord oldBottom = m_selectingBottomRight.GetRow(); // Determine the outer/inner coordinates. - if (oldLeft > leftCol) - { - temp = oldLeft; - oldLeft = leftCol; - leftCol = temp; - } - if (oldTop > topRow ) - { - temp = oldTop; - oldTop = topRow; - topRow = temp; - } - if (oldRight < rightCol ) - { - temp = oldRight; - oldRight = rightCol; - rightCol = temp; - } - if (oldBottom < bottomRow) - { - temp = oldBottom; - oldBottom = bottomRow; - bottomRow = temp; - } + EnsureFirstLessThanSecond(oldLeft, leftCol); + EnsureFirstLessThanSecond(oldTop, topRow); + EnsureFirstLessThanSecond(rightCol, oldRight); + EnsureFirstLessThanSecond(bottomRow, oldBottom); // Now, either the stuff marked old is the outer // rectangle or we don't have a situation where one @@ -7614,27 +7812,6 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(rect); } - -#if 0 - // VZ: my experiments with 3D borders... - - // how to properly set colours for arbitrary bg? - wxCoord x1 = rect.x, - y1 = rect.y, - x2 = rect.x + rect.width - 1, - y2 = rect.y + rect.height - 1; - - dc.SetPen(*wxWHITE_PEN); - dc.DrawLine(x1, y1, x2, y1); - dc.DrawLine(x1, y1, x1, y2); - - dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1); - dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2); - - dc.SetPen(*wxBLACK_PEN); - dc.DrawLine(x1, y2, x2, y2); - dc.DrawLine(x2, y1, x2, y2 + 1); -#endif } wxPen wxGrid::GetDefaultGridLinePen() @@ -8177,8 +8354,6 @@ bool wxGrid::Enable(bool enable) void wxGrid::EnableEditing( bool edit ) { - // TODO: improve this ? - // if ( edit != m_editable ) { if (!edit) @@ -8482,188 +8657,145 @@ void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords ) const } } -// Internal Helper function for computing 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 speed up the search! - -static int CoordToRowOrCol(int coord, int defaultDist, int minDist, - const wxArrayInt& BorderArray, int nMax, - bool clipToMinMax) +// 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::PosToLine(int coord, + bool clipToMinMax, + const wxGridOperations& oper) const { - if (coord < 0) - return clipToMinMax && (nMax > 0) ? 0 : -1; + const int numLines = oper.GetNumberOfLines(this); - if (!defaultDist) - defaultDist = 1; + if ( coord < 0 ) + return clipToMinMax && numLines > 0 ? oper.GetLineAt(this, 0) : -1; - size_t i_max = coord / defaultDist, - i_min = 0; + const int defaultLineSize = oper.GetDefaultLineSize(this); + wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" ); - if (BorderArray.IsEmpty()) - { - if ((int) i_max < nMax) - return i_max; - return clipToMinMax ? nMax - 1 : -1; - } + int maxPos = coord / defaultLineSize, + minPos = 0; - if ( i_max >= BorderArray.GetCount()) - { - i_max = BorderArray.GetCount() - 1; - } - else + // 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 ( coord >= BorderArray[i_max]) - { - i_min = i_max; - if (minDist) - i_max = coord / minDist; - else - i_max = BorderArray.GetCount() - 1; - } - - if ( i_max >= BorderArray.GetCount()) - i_max = BorderArray.GetCount() - 1; - } + if ( maxPos < numLines ) + return maxPos; - if ( coord >= BorderArray[i_max]) - return clipToMinMax ? (int)i_max : -1; - if ( coord < BorderArray[0] ) - return 0; - - while ( i_max - i_min > 0 ) - { - wxCHECK_MSG(BorderArray[i_min] <= coord && coord < BorderArray[i_max], - 0, _T("wxGrid: internal error in CoordToRowOrCol")); - if (coord >= BorderArray[ i_max - 1]) - return i_max; - else - i_max--; - int median = i_min + (i_max - i_min + 1) / 2; - if (coord < BorderArray[median]) - i_max = median; - else - i_min = median; + return clipToMinMax ? numLines - 1 : -1; } - return i_max; -} - -int wxGrid::YToRow( int y ) const -{ - return CoordToRowOrCol(y, m_defaultRowHeight, - m_minAcceptableRowHeight, m_rowBottoms, m_numRows, false); -} - -int wxGrid::XToCol( int x, bool clipToMinMax ) const -{ - if (x < 0) - return clipToMinMax && (m_numCols > 0) ? GetColAt( 0 ) : -1; - - wxASSERT_MSG(m_defaultColWidth > 0, wxT("Default column width can not be zero")); - int maxPos = x / m_defaultColWidth; - int minPos = 0; - - if (m_colRights.IsEmpty()) + // adjust maxPos before starting the binary search + if ( maxPos >= numLines ) { - if(maxPos < m_numCols) - return GetColAt( maxPos ); - return clipToMinMax ? GetColAt( m_numCols - 1 ) : -1; + maxPos = numLines - 1; } - - if ( maxPos >= m_numCols) - maxPos = m_numCols - 1; else { - if ( x >= m_colRights[GetColAt( maxPos )]) + if ( coord >= lineEnds[oper.GetLineAt(this, maxPos)]) { minPos = maxPos; - if (m_minAcceptableColWidth) - maxPos = x / m_minAcceptableColWidth; + const int minDist = oper.GetMinimalAcceptableLineSize(this); + if ( minDist ) + maxPos = coord / minDist; else - maxPos = m_numCols - 1; + maxPos = numLines - 1; } - if ( maxPos >= m_numCols) - maxPos = m_numCols - 1; + + if ( maxPos >= numLines ) + maxPos = numLines - 1; } - //X is beyond the last column - if ( x >= m_colRights[GetColAt( maxPos )]) - return clipToMinMax ? GetColAt( maxPos ) : -1; + // check if the position is beyond the last column + const int lineAtMaxPos = oper.GetLineAt(this, maxPos); + if ( coord >= lineEnds[lineAtMaxPos] ) + return clipToMinMax ? lineAtMaxPos : -1; - //X is before the first column - if ( x < m_colRights[GetColAt( 0 )] ) - return GetColAt( 0 ); + // or before the first one + const int lineAt0 = oper.GetLineAt(this, 0); + if ( coord < lineEnds[lineAt0] ) + return lineAt0; - //Perform a binary search - while ( maxPos - minPos > 0 ) + + // finally do perform the binary search + while ( minPos < maxPos ) { - wxCHECK_MSG(m_colRights[GetColAt( minPos )] <= x && x < m_colRights[GetColAt( maxPos )], - 0, _T("wxGrid: internal error in XToCol")); + wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord && + coord < lineEnds[oper.GetLineAt(this, maxPos)], + -1, + "wxGrid: internal error in PosToLine()" ); - if (x >= m_colRights[GetColAt( maxPos - 1 )]) - return GetColAt( maxPos ); + if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] ) + return oper.GetLineAt(this, maxPos); else maxPos--; - int median = minPos + (maxPos - minPos + 1) / 2; - if (x < m_colRights[GetColAt( median )]) + + const int median = minPos + (maxPos - minPos + 1) / 2; + if ( coord < lineEnds[oper.GetLineAt(this, median)] ) maxPos = median; else minPos = median; } - return GetColAt( maxPos ); + + return oper.GetLineAt(this, maxPos); +} + +int wxGrid::YToRow(int y, bool clipToMinMax) const +{ + return PosToLine(y, clipToMinMax, wxGridRowOperations()); } -// return the row number that that the y coord is near -// the edge of, or -1 if not near an edge. +int wxGrid::XToCol(int x, bool clipToMinMax) const +{ + return PosToLine(x, clipToMinMax, wxGridColumnOperations()); +} + +// return the row number that that the y coord is near the edge of, or -1 if +// not near an edge. +// // coords can only possibly be near an edge if // (a) the row/column is large enough to still allow for an "inner" area -// that is _not_ nead the edge (i.e., if the height/width is smaller +// that is _not_ near the edge (i.e., if the height/width is smaller // than WXGRID_LABEL_EDGE_ZONE, coords are _never_ considered to be // near the edge). // and // (b) resizing rows/columns (the thing for which edge detection is // relevant at all) is enabled. // -int wxGrid::YToEdgeOfRow( int y ) const +int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const { - int i; - i = internalYToRow(y); + if ( !oper.CanResizeLines(this) ) + return -1; - if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE && CanDragRowSize() ) + const int line = oper.PosToLine(this, pos, true); + + if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE ) { - // We know that we are in row i, test whether we are - // close enough to lower or upper border, respectively. - if ( abs(GetRowBottom(i) - y) < WXGRID_LABEL_EDGE_ZONE ) - return i; - else if ( i > 0 && y - GetRowTop(i) < WXGRID_LABEL_EDGE_ZONE ) - return i - 1; + // 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; } -// return the col number that that the x coord is near the edge of, or -// -1 if not near an edge -// See comment at YToEdgeOfRow for conditions on edge detection. -// -int wxGrid::XToEdgeOfCol( int x ) const +int wxGrid::YToEdgeOfRow(int y) const { - int i; - i = internalXToCol(x); - - if ( GetColWidth(i) > WXGRID_LABEL_EDGE_ZONE && CanDragColSize() ) - { - // We know that we are in column i; test whether we are - // close enough to right or left border, respectively. - if ( abs(GetColRight(i) - x) < WXGRID_LABEL_EDGE_ZONE ) - return i; - else if ( i > 0 && x - GetColLeft(i) < WXGRID_LABEL_EDGE_ZONE ) - return i - 1; - } + return PosToEdgeOfLine(y, wxGridRowOperations()); +} - return -1; +int wxGrid::XToEdgeOfCol(int x) const +{ + return PosToEdgeOfLine(x, wxGridColumnOperations()); } wxRect wxGrid::CellToRect( int row, int col ) const @@ -8816,463 +8948,200 @@ void wxGrid::MakeCellVisible( int row, int col ) // ------ Grid cursor movement functions // -bool wxGrid::MoveCursorUp( bool expandSelection ) +bool +wxGrid::DoMoveCursor(bool expandSelection, + const wxGridDirectionOperations& diroper) { - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() >= 0 ) + if ( m_currentCellCoords == wxGridNoCellCoords ) + return false; + + if ( expandSelection ) { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() > 0 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() - 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() > 0 ) - { - int row = m_currentCellCoords.GetRow() - 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; + if ( m_selectingKeyboard == wxGridNoCellCoords ) + m_selectingKeyboard = m_currentCellCoords; - return true; - } + if ( diroper.IsAtBoundary(m_selectingKeyboard) ) + return false; - return false; -} + diroper.Advance(m_selectingKeyboard); -bool wxGrid::MoveCursorDown( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() < m_numRows ) + MakeCellVisible(m_selectingKeyboard); + HighlightBlock(m_currentCellCoords, m_selectingKeyboard); + } + else { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() < m_numRows - 1 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() + 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() < m_numRows - 1 ) - { - int row = m_currentCellCoords.GetRow() + 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else + if ( diroper.IsAtBoundary(m_currentCellCoords) ) return false; - return true; + ClearSelection(); + + wxGridCellCoords coords = m_currentCellCoords; + diroper.Advance(coords); + MakeCellVisible(coords); + SetCurrentCell(coords); } - return false; + return true; } -bool wxGrid::MoveCursorLeft( bool expandSelection ) +bool wxGrid::MoveCursorUp(bool expandSelection) { - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() >= 0 ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetCol() > 0 ) - { - m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() - 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( GetColPos( m_currentCellCoords.GetCol() ) > 0 ) - { - int row = m_currentCellCoords.GetRow(); - int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) - 1 ); - ClearSelection(); - - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; + return DoMoveCursor(expandSelection, + wxGridBackwardOperations(this, wxGridRowOperations())); } -bool wxGrid::MoveCursorRight( bool expandSelection ) +bool wxGrid::MoveCursorDown(bool expandSelection) { - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() < m_numCols ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetCol() < m_numCols - 1 ) - { - m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() + 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( GetColPos( m_currentCellCoords.GetCol() ) < m_numCols - 1 ) - { - int row = m_currentCellCoords.GetRow(); - int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) + 1 ); - ClearSelection(); - - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; + return DoMoveCursor(expandSelection, + wxGridForwardOperations(this, wxGridRowOperations())); +} - return true; - } +bool wxGrid::MoveCursorLeft(bool expandSelection) +{ + return DoMoveCursor(expandSelection, + wxGridBackwardOperations(this, wxGridColumnOperations())); +} - return false; +bool wxGrid::MoveCursorRight(bool expandSelection) +{ + return DoMoveCursor(expandSelection, + wxGridForwardOperations(this, wxGridColumnOperations())); } -bool wxGrid::MovePageUp() +bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper) { if ( m_currentCellCoords == wxGridNoCellCoords ) return false; - int row = m_currentCellCoords.GetRow(); - if ( row > 0 ) - { - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - - int y = GetRowTop(row); - int newRow = internalYToRow( y - ch + 1 ); + if ( diroper.IsAtBoundary(m_currentCellCoords) ) + return false; - if ( newRow == row ) - { - // row > 0, so newRow can never be less than 0 here. - newRow = row - 1; - } + const int oldRow = m_currentCellCoords.GetRow(); + int newRow = diroper.MoveByPixelDistance(oldRow, m_gridWin->GetClientSize().y); + if ( newRow == oldRow ) + { + wxGridCellCoords coords(m_currentCellCoords); + diroper.Advance(coords); + newRow = coords.GetRow(); + } - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); + MakeCellVisible(newRow, m_currentCellCoords.GetCol()); + SetCurrentCell(newRow, m_currentCellCoords.GetCol()); - return true; - } + return true; +} - return false; +bool wxGrid::MovePageUp() +{ + return DoMoveCursorByPage( + wxGridBackwardOperations(this, wxGridRowOperations())); } bool wxGrid::MovePageDown() { - if ( m_currentCellCoords == wxGridNoCellCoords ) - return false; + return DoMoveCursorByPage( + wxGridForwardOperations(this, wxGridColumnOperations())); +} - int row = m_currentCellCoords.GetRow(); - if ( (row + 1) < m_numRows ) +// helper of DoMoveCursorByBlock(): advance the cell coordinates using diroper +// until we find a non-empty cell or reach the grid end +void +wxGrid::AdvanceToNextNonEmpty(wxGridCellCoords& coords, + const wxGridDirectionOperations& diroper) +{ + while ( !diroper.IsAtBoundary(coords) ) { - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - - int y = GetRowTop(row); - int newRow = internalYToRow( y + ch ); - if ( newRow == row ) - { - // row < m_numRows, so newRow can't overflow here. - newRow = row + 1; - } - - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); - - return true; + diroper.Advance(coords); + if ( !m_table->IsEmpty(coords) ) + break; } - - return false; } -bool wxGrid::MoveCursorUpBlock( bool expandSelection ) +bool +wxGrid::DoMoveCursorByBlock(bool expandSelection, + const wxGridDirectionOperations& diroper) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() > 0 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( row > 0 ) - { - row--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row - 1, col) ) - { - // starting at the top of a block: find the next block - // - row--; - while ( row > 0 ) - { - row--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the top of the block - // - while ( row > 0 ) - { - row--; - if ( m_table->IsEmptyCell(row, col) ) - { - row++; - break; - } - } - } + if ( !m_table || m_currentCellCoords == wxGridNoCellCoords ) + return false; - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } + if ( diroper.IsAtBoundary(m_currentCellCoords) ) + return false; - return true; + wxGridCellCoords coords(m_currentCellCoords); + if ( m_table->IsEmpty(coords) ) + { + // we are in an empty cell: find the next block of non-empty cells + AdvanceToNextNonEmpty(coords, diroper); } - - return false; -} - -bool wxGrid::MoveCursorDownBlock( bool expandSelection ) -{ - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() < m_numRows - 1 ) + else // current cell is not empty { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) + diroper.Advance(coords); + if ( m_table->IsEmpty(coords) ) { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( row < m_numRows - 1 ) - { - row++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row + 1, col) ) - { - // starting at the bottom of a block: find the next block - // - row++; - while ( row < m_numRows - 1 ) - { - row++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } + // we started at the end of a block, find the next one + AdvanceToNextNonEmpty(coords, diroper); } - else + else // we're in a middle of a block { - // starting within a block: find the bottom of the block - // - while ( row < m_numRows - 1 ) + // go to the end of it, i.e. find the last cell before the next + // empty one + while ( !diroper.IsAtBoundary(coords) ) { - row++; - if ( m_table->IsEmptyCell(row, col) ) - { - row--; + wxGridCellCoords coordsNext(coords); + diroper.Advance(coordsNext); + if ( m_table->IsEmpty(coordsNext) ) break; - } - } - } - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); + coords = coordsNext; + } } + } - return true; + MakeCellVisible(coords); + if ( expandSelection ) + { + m_selectingKeyboard = coords; + HighlightBlock(m_currentCellCoords, m_selectingKeyboard); + } + else + { + ClearSelection(); + SetCurrentCell(coords); } - return false; + return true; } -bool wxGrid::MoveCursorLeftBlock( bool expandSelection ) +bool wxGrid::MoveCursorUpBlock(bool expandSelection) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() > 0 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( col > 0 ) - { - col--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row, col - 1) ) - { - // starting at the left of a block: find the next block - // - col--; - while ( col > 0 ) - { - col--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the left of the block - // - while ( col > 0 ) - { - col--; - if ( m_table->IsEmptyCell(row, col) ) - { - col++; - break; - } - } - } - - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridRowOperations()) + ); +} - return true; - } +bool wxGrid::MoveCursorDownBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridRowOperations()) + ); +} - return false; +bool wxGrid::MoveCursorLeftBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridColumnOperations()) + ); } bool wxGrid::MoveCursorRightBlock( bool expandSelection ) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() < m_numCols - 1 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( col < m_numCols - 1 ) - { - col++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row, col + 1) ) - { - // starting at the right of a block: find the next block - // - col++; - while ( col < m_numCols - 1 ) - { - col++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the right of the block - // - while ( col < m_numCols - 1 ) - { - col++; - if ( m_table->IsEmptyCell(row, col) ) - { - col--; - break; - } - } - } - - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } - - return true; - } - - return false; + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridColumnOperations()) + ); } // @@ -9908,7 +9777,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const if ( !LookupAttr(row, col, &attr) ) { attr = m_table ? m_table->GetAttr(row, col, wxGridCellAttr::Any) - : (wxGridCellAttr *)NULL; + : NULL; CacheAttr(row, col, attr); } } @@ -9928,7 +9797,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; bool canHave = ((wxGrid*)this)->CanHaveAttributes(); wxCHECK_MSG( canHave, attr, _T("Cell attributes not allowed")); @@ -10804,46 +10673,40 @@ void wxGrid::SelectAll() // cell, row and col deselection // ---------------------------------------------------------------------------- -void wxGrid::DeselectRow( int row ) +void wxGrid::DeselectLine(int line, const wxGridOperations& oper) { if ( !m_selection ) return; - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows ) + const wxGridSelectionModes mode = m_selection->GetSelectionMode(); + if ( mode == oper.GetSelectionMode() ) { - if ( m_selection->IsInSelection(row, 0 ) ) - m_selection->ToggleCellSelection(row, 0); + const wxGridCellCoords c(oper.MakeCoords(line, 0)); + if ( m_selection->IsInSelection(c) ) + m_selection->ToggleCellSelection(c); } - else + else if ( mode != oper.Dual().GetSelectionMode() ) { - int nCols = GetNumberCols(); - for ( int i = 0; i < nCols; i++ ) + const int nOther = oper.Dual().GetNumberOfLines(this); + for ( int i = 0; i < nOther; i++ ) { - if ( m_selection->IsInSelection(row, i ) ) - m_selection->ToggleCellSelection(row, i); + const wxGridCellCoords c(oper.MakeCoords(line, i)); + if ( m_selection->IsInSelection(c) ) + m_selection->ToggleCellSelection(c); } } + //else: can only select orthogonal lines so no lines in this direction + // could have been selected anyhow } -void wxGrid::DeselectCol( int col ) +void wxGrid::DeselectRow(int row) { - if ( !m_selection ) - return; + DeselectLine(row, wxGridRowOperations()); +} - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns ) - { - if ( m_selection->IsInSelection(0, col ) ) - m_selection->ToggleCellSelection(0, col); - } - else - { - int nRows = GetNumberRows(); - for ( int i = 0; i < nRows; i++ ) - { - if ( m_selection->IsInSelection(i, col ) ) - m_selection->ToggleCellSelection(i, col); - } - } +void wxGrid::DeselectCol(int col) +{ + DeselectLine(col, wxGridColumnOperations()); } void wxGrid::DeselectCell( int row, int col )