From 02509cbc39015540cb5dbbf7f67b1cda737d4d36 Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Tue, 3 Mar 2020 00:09:50 +0700 Subject: [PATCH] Refactor wxGridSelection to store selection as blocks only Store all types of selection with an array of blocks instead of arrays of cells, blocks, rows and columns. It (hopefully) simplifies the code and allows us to implement editing of the last selection block much easier. --- include/wx/generic/gridsel.h | 82 +-- interface/wx/grid.h | 2 +- src/generic/gridsel.cpp | 1243 ++++++++++------------------------ 3 files changed, 381 insertions(+), 946 deletions(-) diff --git a/include/wx/generic/gridsel.h b/include/wx/generic/gridsel.h index 4e9f66dd67..18016adfcd 100644 --- a/include/wx/generic/gridsel.h +++ b/include/wx/generic/gridsel.h @@ -17,6 +17,10 @@ #include "wx/grid.h" +#include "wx/vector.h" + +typedef wxVector wxVectorGridBlockCoords; + class WXDLLIMPEXP_CORE wxGridSelection { public: @@ -48,16 +52,6 @@ public: kbd, sendEvent); } - void SelectCell(int row, int col, - const wxKeyboardState& kbd = wxKeyboardState(), - bool sendEvent = true); - void SelectCell(const wxGridCellCoords& coords, - const wxKeyboardState& kbd = wxKeyboardState(), - bool sendEvent = true) - { - SelectCell(coords.GetRow(), coords.GetCol(), kbd, sendEvent); - } - void ToggleCellSelection(int row, int col, const wxKeyboardState& kbd = wxKeyboardState()); void ToggleCellSelection(const wxGridCellCoords& coords, @@ -66,35 +60,20 @@ public: ToggleCellSelection(coords.GetRow(), coords.GetCol(), kbd); } + void DeselectBlock(const wxGridBlockCoords& block, + const wxKeyboardState& kbd = wxKeyboardState(), + bool sendEvent = true ); + void ClearSelection(); void UpdateRows( size_t pos, int numRows ); void UpdateCols( size_t pos, int numCols ); - const wxGridCellCoordsArray& GetCellSelection() const - { - return m_cellSelection; - } - - const wxGridCellCoordsArray& GetBlockSelectionTopLeft() const - { - return m_blockSelectionTopLeft; - } - - const wxGridCellCoordsArray& GetBlockSelectionBottomRight() const - { - return m_blockSelectionBottomRight; - } - - const wxArrayInt& GetRowSelection() const - { - return m_rowSelection; - } - - const wxArrayInt& GetColSelection() const - { - return m_colSelection; - } + wxGridCellCoordsArray GetCellSelection() const; + wxGridCellCoordsArray GetBlockSelectionTopLeft() const; + wxGridCellCoordsArray GetBlockSelectionBottomRight() const; + wxArrayInt GetRowSelection() const; + wxArrayInt GetColSelection() const; private: int BlockContain( int topRow1, int leftCol1, @@ -105,28 +84,27 @@ private: // -1, if Block2 contains Block1, // 0, otherwise - int BlockContainsCell( int topRow, int leftCol, - int bottomRow, int rightCol, - int row, int col ) - // returns 1, if Block contains Cell, - // 0, otherwise + void SelectBlockNoEvent(const wxGridBlockCoords& block) { - return ( topRow <= row && row <= bottomRow && - leftCol <= col && col <= rightCol ); - } - - void SelectBlockNoEvent(int topRow, int leftCol, - int bottomRow, int rightCol) - { - SelectBlock(topRow, leftCol, bottomRow, rightCol, + SelectBlock(block.GetTopRow(), block.GetLeftCol(), + block.GetBottomRow(), block.GetRightCol(), wxKeyboardState(), false); } - wxGridCellCoordsArray m_cellSelection; - wxGridCellCoordsArray m_blockSelectionTopLeft; - wxGridCellCoordsArray m_blockSelectionBottomRight; - wxArrayInt m_rowSelection; - wxArrayInt m_colSelection; + // Really select the block and don't check for the current selection mode. + void Select(const wxGridBlockCoords& block, + const wxKeyboardState& kbd, bool sendEvent); + + // If the new block containing any of the passed blocks, remove them. + // if a new block contained in the passed blockc, return. + // Otherwise add the new block to the blocks array. + void MergeOrAddBlock(wxVectorGridBlockCoords& blocks, + const wxGridBlockCoords& block); + + // The vector of selection blocks. We expect that the users select + // relatively small amount of blocks. K-D tree can be used to speed up + // searching speed. + wxVectorGridBlockCoords m_selection; wxGrid *m_grid; wxGrid::wxGridSelectionModes m_selectionMode; diff --git a/interface/wx/grid.h b/interface/wx/grid.h index 1bbad92e42..060f913663 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -1904,7 +1904,7 @@ public: wxGridBlockCoords Canonicalize() const; /** - Whether the blocks intersects. + Whether the blocks intersect. @return @true, if the block intersects with the other, @false, otherwise. diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp index 0bda0caa44..20b24f1c52 100644 --- a/src/generic/gridsel.cpp +++ b/src/generic/gridsel.cpp @@ -26,16 +26,22 @@ #if wxUSE_GRID #include "wx/generic/gridsel.h" +#include "wx/dynarray.h" -// Some explanation for the members of the class: -// m_cellSelection stores individual selected cells -// -- this is only used if m_selectionMode == wxGridSelectCells -// m_blockSelectionTopLeft and m_blockSelectionBottomRight -// store the upper left and lower right corner of selected Blocks -// m_rowSelection and m_colSelection store individual selected -// rows and columns; maybe those are superfluous and should be -// treated as blocks? +namespace +{ + +// The helper function to compare wxIntSortedArray elements. +int CompareInts(int n1, int n2) +{ + return n1 - n2; +} + +} + +WX_DEFINE_SORTED_ARRAY(int, wxIntSortedArray); + wxGridSelection::wxGridSelection( wxGrid * grid, wxGrid::wxGridSelectionModes sel ) @@ -46,66 +52,19 @@ wxGridSelection::wxGridSelection( wxGrid * grid, bool wxGridSelection::IsSelection() { - return ( m_cellSelection.GetCount() || m_blockSelectionTopLeft.GetCount() || - m_rowSelection.GetCount() || m_colSelection.GetCount() ); + return !m_selection.empty(); } bool wxGridSelection::IsInSelection( int row, int col ) { - size_t count; - - // First check whether the given cell is individually selected - // (if m_selectionMode is wxGridSelectCells). - if ( m_selectionMode == wxGrid::wxGridSelectCells ) - { - count = m_cellSelection.GetCount(); - for ( size_t n = 0; n < count; n++ ) - { - wxGridCellCoords& coords = m_cellSelection[n]; - if ( row == coords.GetRow() && col == coords.GetCol() ) - return true; - } - } - - // Now check whether the given cell is - // contained in one of the selected blocks. - count = m_blockSelectionTopLeft.GetCount(); + // Check whether the given cell is contained in one of the selected blocks. + const size_t count = m_selection.size(); for ( size_t n = 0; n < count; n++ ) { - wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n]; - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - if ( BlockContainsCell(coords1.GetRow(), coords1.GetCol(), - coords2.GetRow(), coords2.GetCol(), - row, col ) ) + if ( m_selection[n].ContainCell(wxGridCellCoords(row, col)) ) return true; } - // Now check whether the given cell is - // contained in one of the selected rows - // (unless we are in column selection mode). - if ( m_selectionMode != wxGrid::wxGridSelectColumns ) - { - count = m_rowSelection.GetCount(); - for ( size_t n = 0; n < count; n++ ) - { - if ( row == m_rowSelection[n] ) - return true; - } - } - - // Now check whether the given cell is - // contained in one of the selected columns - // (unless we are in row selection mode). - if ( m_selectionMode != wxGrid::wxGridSelectRows ) - { - count = m_colSelection.GetCount(); - for ( size_t n = 0; n < count; n++ ) - { - if ( col == m_colSelection[n] ) - return true; - } - } - return false; } @@ -116,6 +75,8 @@ void wxGridSelection::SetSelectionMode( wxGrid::wxGridSelectionModes selmode ) if (selmode == m_selectionMode) return; + // TODO: wxGridSelectRowsOrColumns? + if ( m_selectionMode != wxGrid::wxGridSelectCells ) { // if changing form row to column selection @@ -127,51 +88,34 @@ void wxGridSelection::SetSelectionMode( wxGrid::wxGridSelectionModes selmode ) } else { - // if changing from cell selection to something else, - // promote selected cells/blocks to whole rows/columns. - size_t n; - while ( ( n = m_cellSelection.GetCount() ) > 0 ) - { - n--; - wxGridCellCoords& coords = m_cellSelection[n]; - int row = coords.GetRow(); - int col = coords.GetCol(); - m_cellSelection.RemoveAt(n); - if (selmode == wxGrid::wxGridSelectRows) - SelectRow( row ); - else // selmode == wxGridSelectColumns) - SelectCol( col ); - } - // Note that m_blockSelectionTopLeft's size may be changing! - for ( n = m_blockSelectionTopLeft.GetCount(); n > 0; ) + for ( size_t n = m_selection.size(); n > 0; ) { n--; - wxGridCellCoords& coords = m_blockSelectionTopLeft[n]; - int topRow = coords.GetRow(); - int leftCol = coords.GetCol(); - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - int bottomRow = coords2.GetRow(); - int rightCol = coords2.GetCol(); + const wxGridBlockCoords& block = m_selection[n]; + const int topRow = block.GetTopRow(); + const int leftCol = block.GetLeftCol(); + const int bottomRow = block.GetBottomRow(); + const int rightCol = block.GetRightCol(); if (selmode == wxGrid::wxGridSelectRows) { if (leftCol != 0 || rightCol != m_grid->GetNumberCols() - 1 ) { - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); - SelectBlockNoEvent( topRow, 0, - bottomRow, m_grid->GetNumberCols() - 1); + m_selection.erase(m_selection.begin() + n); + SelectBlockNoEvent( + wxGridBlockCoords(topRow, 0, + bottomRow, m_grid->GetNumberCols() - 1)); } } else // selmode == wxGridSelectColumns) { if (topRow != 0 || bottomRow != m_grid->GetNumberRows() - 1 ) { - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); - SelectBlockNoEvent(0, leftCol, - m_grid->GetNumberRows() - 1, rightCol); + m_selection.erase(m_selection.begin() + n); + SelectBlockNoEvent( + wxGridBlockCoords(0, leftCol, + m_grid->GetNumberRows() - 1, rightCol)); } } } @@ -185,185 +129,17 @@ void wxGridSelection::SelectRow(int row, const wxKeyboardState& kbd) if ( m_selectionMode == wxGrid::wxGridSelectColumns ) return; - size_t count, n; - - // Remove single cells contained in newly selected block. - if ( m_selectionMode == wxGrid::wxGridSelectCells ) - { - count = m_cellSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords = m_cellSelection[n]; - if ( BlockContainsCell( row, 0, row, m_grid->GetNumberCols() - 1, - coords.GetRow(), coords.GetCol() ) ) - { - m_cellSelection.RemoveAt(n); - n--; - count--; - } - } - } - - // Simplify list of selected blocks (if possible) - count = m_blockSelectionTopLeft.GetCount(); - bool done = false; - - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n]; - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - - // Remove block if it is a subset of the row - if ( coords1.GetRow() == row && row == coords2.GetRow() ) - { - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); - n--; - count--; - } - else if ( coords1.GetCol() == 0 && - coords2.GetCol() == m_grid->GetNumberCols() - 1 ) - { - // silently return, if row is contained in block - if ( coords1.GetRow() <= row && row <= coords2.GetRow() ) - return; - // expand block, if it touched row - else if ( coords1.GetRow() == row + 1) - { - coords1.SetRow(row); - done = true; - } - else if ( coords2.GetRow() == row - 1) - { - coords2.SetRow(row); - done = true; - } - } - } - - // Unless we successfully handled the row, - // check whether row is already selected. - if ( !done ) - { - count = m_rowSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - if ( row == m_rowSelection[n] ) - return; - } - - // Add row to selection - m_rowSelection.Add(row); - } - - // Update View: - if ( !m_grid->GetBatchCount() && m_grid->GetNumberCols() != 0 ) - { - m_grid->RefreshBlock(row, 0, row, m_grid->GetNumberCols() - 1); - } - - // Send Event - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - wxGridCellCoords( row, 0 ), - wxGridCellCoords( row, m_grid->GetNumberCols() - 1 ), - true, - kbd); - - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); + Select(wxGridBlockCoords(row, 0, row, m_grid->GetNumberCols() - 1), + kbd, true); } void wxGridSelection::SelectCol(int col, const wxKeyboardState& kbd) { if ( m_selectionMode == wxGrid::wxGridSelectRows ) return; - size_t count, n; - // Remove single cells contained in newly selected block. - if ( m_selectionMode == wxGrid::wxGridSelectCells ) - { - count = m_cellSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords = m_cellSelection[n]; - if ( BlockContainsCell( 0, col, m_grid->GetNumberRows() - 1, col, - coords.GetRow(), coords.GetCol() ) ) - { - m_cellSelection.RemoveAt(n); - n--; - count--; - } - } - } - - // Simplify list of selected blocks (if possible) - count = m_blockSelectionTopLeft.GetCount(); - bool done = false; - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n]; - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - - // Remove block if it is a subset of the column - if ( coords1.GetCol() == col && col == coords2.GetCol() ) - { - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); - n--; - count--; - } - else if ( coords1.GetRow() == 0 && - coords2.GetRow() == m_grid->GetNumberRows() - 1 ) - { - // silently return, if row is contained in block - if ( coords1.GetCol() <= col && col <= coords2.GetCol() ) - return; - // expand block, if it touched col - else if ( coords1.GetCol() == col + 1) - { - coords1.SetCol(col); - done = true; - } - else if ( coords2.GetCol() == col - 1) - { - coords2.SetCol(col); - done = true; - } - } - } - - // Unless we successfully handled the column, - // Check whether col is already selected. - if ( !done ) - { - count = m_colSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - if ( col == m_colSelection[n] ) - return; - } - - // Add col to selection - m_colSelection.Add(col); - } - - // Update View: - if ( !m_grid->GetBatchCount() && m_grid->GetNumberRows() != 0 ) - { - m_grid->RefreshBlock(0, col, m_grid->GetNumberRows() - 1, col); - } - - // Send Event - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - wxGridCellCoords( 0, col ), - wxGridCellCoords( m_grid->GetNumberRows() - 1, col ), - true, - kbd ); - - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); + Select(wxGridBlockCoords(0, col, m_grid->GetNumberRows() - 1, col), + kbd, true); } void wxGridSelection::SelectBlock( int topRow, int leftCol, @@ -372,264 +148,39 @@ void wxGridSelection::SelectBlock( int topRow, int leftCol, bool sendEvent ) { // Fix the coordinates of the block if needed. + int allowed = -1; switch ( m_selectionMode ) { - default: - wxFAIL_MSG( "unknown selection mode" ); - wxFALLTHROUGH; - case wxGrid::wxGridSelectCells: - // nothing to do -- in this mode arbitrary blocks can be selected + // In this mode arbitrary blocks can be selected. + allowed = 1; break; case wxGrid::wxGridSelectRows: leftCol = 0; rightCol = m_grid->GetNumberCols() - 1; + allowed = 1; break; case wxGrid::wxGridSelectColumns: topRow = 0; bottomRow = m_grid->GetNumberRows() - 1; + allowed = 1; break; case wxGrid::wxGridSelectRowsOrColumns: // block selection doesn't make sense for this mode, we could only // select the entire grid but this wouldn't be useful - return; + allowed = 0; + break; } - if ( topRow > bottomRow ) - { - int temp = topRow; - topRow = bottomRow; - bottomRow = temp; - } - - if ( leftCol > rightCol ) - { - int temp = leftCol; - leftCol = rightCol; - rightCol = temp; - } - - // Handle single cell selection in SelectCell. - // (MB: added check for selection mode here to prevent - // crashes if, for example, we are select rows and the - // grid only has 1 col) - if ( m_selectionMode == wxGrid::wxGridSelectCells && - topRow == bottomRow && leftCol == rightCol ) - { - SelectCell( topRow, leftCol, kbd, sendEvent ); - } - - size_t count, n; - - if ( m_selectionMode == wxGrid::wxGridSelectRows ) - { - // find out which rows are already selected: - wxArrayInt alreadyselected; - alreadyselected.Add(0,bottomRow-topRow+1); - for( n = 0; n < m_rowSelection.GetCount(); n++) - { - int row = m_rowSelection[n]; - if( (row >= topRow) && (row <= bottomRow) ) - { - alreadyselected[ row - topRow ]=1; - } - } - - // add the newly selected rows: - for ( int row = topRow; row <= bottomRow; row++ ) - { - if ( alreadyselected[ row - topRow ] == 0 ) - { - m_rowSelection.Add( row ); - } - } - } - else if ( m_selectionMode == wxGrid::wxGridSelectColumns ) - { - // find out which columns are already selected: - wxArrayInt alreadyselected; - alreadyselected.Add(0,rightCol-leftCol+1); - for( n = 0; n < m_colSelection.GetCount(); n++) - { - int col = m_colSelection[n]; - if( (col >= leftCol) && (col <= rightCol) ) - { - alreadyselected[ col - leftCol ]=1; - } - } - - // add the newly selected columns: - for ( int col = leftCol; col <= rightCol; col++ ) - { - if ( alreadyselected[ col - leftCol ] == 0 ) - { - m_colSelection.Add( col ); - } - } - } - else - { - // Remove single cells contained in newly selected block. - if ( m_selectionMode == wxGrid::wxGridSelectCells ) - { - count = m_cellSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords = m_cellSelection[n]; - if ( BlockContainsCell( topRow, leftCol, bottomRow, rightCol, - coords.GetRow(), coords.GetCol() ) ) - { - m_cellSelection.RemoveAt(n); - n--; - count--; - } - } - } - - // If a block containing the selection is already selected, return, - // if a block contained in the selection is found, remove it. - - count = m_blockSelectionTopLeft.GetCount(); - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n]; - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - - switch ( BlockContain( coords1.GetRow(), coords1.GetCol(), - coords2.GetRow(), coords2.GetCol(), - topRow, leftCol, bottomRow, rightCol ) ) - { - case 1: - return; - - case -1: - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); - n--; - count--; - break; - - default: - break; - } - } - - // If a row containing the selection is already selected, return, - // if a row contained in newly selected block is found, remove it. - count = m_rowSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - switch ( BlockContain( m_rowSelection[n], 0, - m_rowSelection[n], m_grid->GetNumberCols() - 1, - topRow, leftCol, bottomRow, rightCol ) ) - { - case 1: - return; - - case -1: - m_rowSelection.RemoveAt(n); - n--; - count--; - break; - - default: - break; - } - } - - // Same for columns. - count = m_colSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - switch ( BlockContain( 0, m_colSelection[n], - m_grid->GetNumberRows() - 1, m_colSelection[n], - topRow, leftCol, bottomRow, rightCol ) ) - { - case 1: - return; - - case -1: - m_colSelection.RemoveAt(n); - n--; - count--; - break; - - default: - break; - } - } - - m_blockSelectionTopLeft.Add( wxGridCellCoords( topRow, leftCol ) ); - m_blockSelectionBottomRight.Add( wxGridCellCoords( bottomRow, rightCol ) ); - } - // Update View: - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(topRow, leftCol, bottomRow, rightCol); - } - - // Send Event, if not disabled. - if ( sendEvent ) - { - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - wxGridCellCoords( topRow, leftCol ), - wxGridCellCoords( bottomRow, rightCol ), - true, - kbd); - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); - } -} - -void wxGridSelection::SelectCell( int row, int col, - const wxKeyboardState& kbd, - bool sendEvent ) -{ - if ( IsInSelection ( row, col ) ) + wxASSERT_MSG(allowed != -1, "unknown selection mode?"); + if ( !allowed ) return; - wxGridCellCoords selectedTopLeft, selectedBottomRight; - if ( m_selectionMode == wxGrid::wxGridSelectRows ) - { - m_rowSelection.Add( row ); - selectedTopLeft = wxGridCellCoords( row, 0 ); - selectedBottomRight = wxGridCellCoords( row, m_grid->GetNumberCols() - 1 ); - } - else if ( m_selectionMode == wxGrid::wxGridSelectColumns ) - { - m_colSelection.Add( col ); - selectedTopLeft = wxGridCellCoords( 0, col ); - selectedBottomRight = wxGridCellCoords( m_grid->GetNumberRows() - 1, col ); - } - else - { - m_cellSelection.Add( wxGridCellCoords( row, col ) ); - selectedTopLeft = wxGridCellCoords( row, col ); - selectedBottomRight = wxGridCellCoords( row, col ); - } - - // Update View: - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(selectedTopLeft, selectedBottomRight); - } - - // Send event - if (sendEvent) - { - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - selectedTopLeft, - selectedBottomRight, - true, - kbd); - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); - } + Select(wxGridBlockCoords(topRow, leftCol, bottomRow, rightCol).Canonicalize(), + kbd, sendEvent); } void @@ -639,239 +190,146 @@ wxGridSelection::ToggleCellSelection(int row, int col, // if the cell is not selected, select it if ( !IsInSelection ( row, col ) ) { - SelectCell(row, col, kbd); + SelectBlock(row, col, row, col, kbd); return; } - // otherwise deselect it. This can be simple or more or - // less difficult, depending on how the cell is selected. + // otherwise deselect it. + DeselectBlock(wxGridBlockCoords(row, col, row, col), kbd); +} + + +void +wxGridSelection::DeselectBlock(const wxGridBlockCoords& block, + const wxKeyboardState& kbd, + bool sendEvent) +{ + const wxGridBlockCoords canonicalizedBlock = block.Canonicalize(); + size_t count, n; - // The simplest case: The cell is contained in m_cellSelection - // Then it can't be contained in rows/cols/block (since those - // would remove the cell from m_cellSelection on creation), so - // we just have to remove it from m_cellSelection. - - if ( m_selectionMode == wxGrid::wxGridSelectCells ) - { - count = m_cellSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - const wxGridCellCoords& sel = m_cellSelection[n]; - if ( row == sel.GetRow() && col == sel.GetCol() ) - { - wxGridCellCoords coords = m_cellSelection[n]; - m_cellSelection.RemoveAt(n); - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(coords, coords); - } - - // Send event - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - wxGridCellCoords( row, col ), - wxGridCellCoords( row, col ), - false, - kbd ); - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); - - return; - } - } - } - - // The most difficult case: The cell is member of one or even several - // blocks. Split each such block in up to 4 new parts, that don't - // contain the cell to be selected, like this: + // If the selected block intersects with the deselection block, split it + // in up to 4 new parts, that don't contain the block to be selected, like + // this (for rows): // |---------------------------| // | | // | part 1 | // | | // |---------------------------| - // | part 3 |x| part 4 | + // | part 3 | x | part 4 | // |---------------------------| // | | // | part 2 | // | | // |---------------------------| - // (The x marks the newly deselected cell). - // Note: in row selection mode, we only need part1 and part2; - // in column selection mode, we only need part 3 and part4, - // which are expanded to whole columns automatically! + // And for columns: + // |---------------------------| + // | | | | + // | | part 3 | | + // | | | | + // | |---------| | + // | part 1 | x | part 2 | + // | |---------| | + // | | | | + // | | part 4 | | + // | | | | + // |---------------------------| + // (The x marks the newly deselected block). + // Note: in row/column selection mode, we only need part1 and part2 - count = m_blockSelectionTopLeft.GetCount(); + // Blocks to refresh. + wxVectorGridBlockCoords refreshBlocks; + // Add the deselected block. + refreshBlocks.push_back(canonicalizedBlock); + + count = m_selection.size(); for ( n = 0; n < count; n++ ) { - wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n]; - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - int topRow = coords1.GetRow(); - int leftCol = coords1.GetCol(); - int bottomRow = coords2.GetRow(); - int rightCol = coords2.GetCol(); + const wxGridBlockCoords& selBlock = m_selection[n]; - if ( BlockContainsCell( topRow, leftCol, bottomRow, rightCol, row, col ) ) + // Whether blocks intersect. + if ( !m_selection[n].Intersects(canonicalizedBlock) ) + continue; + + int splitOrientation = wxHORIZONTAL; + switch ( m_selectionMode ) { - // remove the block - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); - n--; - count--; + case wxGrid::wxGridSelectCells: + if ( selBlock.GetLeftCol() == 0 && + selBlock.GetRightCol() == m_grid->GetNumberCols() - 1 ) + break; - // add up to 4 smaller blocks and set update region - if ( m_selectionMode != wxGrid::wxGridSelectColumns ) - { - if ( topRow < row ) - SelectBlockNoEvent(topRow, leftCol, row - 1, rightCol); - if ( bottomRow > row ) - SelectBlockNoEvent(row + 1, leftCol, bottomRow, rightCol); - } + if ( selBlock.GetTopRow() == 0 && + selBlock.GetBottomRow() == m_grid->GetNumberRows() - 1 ) + splitOrientation = wxVERTICAL; - if ( m_selectionMode != wxGrid::wxGridSelectRows ) + break; + + case wxGrid::wxGridSelectColumns: + splitOrientation = wxVERTICAL; + break; + + case wxGrid::wxGridSelectRowsOrColumns: + if ( selBlock.GetLeftCol() == 0 && + selBlock.GetRightCol() == m_grid->GetNumberCols() - 1 ) + break; + + splitOrientation = wxVERTICAL; + break; + } + + // remove the block + m_selection.erase(m_selection.begin() + n); + n--; + count--; + + wxGridBlockDiffResult result = + selBlock.Difference(canonicalizedBlock, splitOrientation); + + for ( int i = 0; i < 2; ++i ) + { + const wxGridBlockCoords& part = result.m_parts[i]; + if ( part != wxGridNoBlockCoords ) + SelectBlockNoEvent(part); + } + + for ( int i = 2; i < 4; ++i ) + { + const wxGridBlockCoords& part = result.m_parts[i]; + if ( part != wxGridNoBlockCoords ) { - if ( leftCol < col ) - SelectBlockNoEvent(row, leftCol, row, col - 1); - if ( rightCol > col ) - SelectBlockNoEvent(row, col + 1, row, rightCol); + // Add part[2] and part[3] only in the cells selection mode. + if ( m_selectionMode == wxGrid::wxGridSelectCells ) + SelectBlockNoEvent(part); + else + MergeOrAddBlock(refreshBlocks, part); } } } - bool rowSelectionWasChanged = false; - // remove a cell from a row, adding up to two new blocks - if ( m_selectionMode != wxGrid::wxGridSelectColumns ) + // Refresh the screen and send events. + count = refreshBlocks.size(); + for ( n = 0; n < count; n++ ) { - count = m_rowSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - if ( m_rowSelection[n] == row ) - { - m_rowSelection.RemoveAt(n); - n--; - count--; + const wxGridBlockCoords& refBlock = refreshBlocks[n]; - rowSelectionWasChanged = true; - - if (m_selectionMode == wxGrid::wxGridSelectCells) - { - if ( col > 0 ) - SelectBlockNoEvent(row, 0, row, col - 1); - if ( col < m_grid->GetNumberCols() - 1 ) - SelectBlockNoEvent( row, col + 1, - row, m_grid->GetNumberCols() - 1); - } - } - } - } - - bool colSelectionWasChanged = false; - // remove a cell from a column, adding up to two new blocks - if ( m_selectionMode != wxGrid::wxGridSelectRows ) - { - count = m_colSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - if ( m_colSelection[n] == col ) - { - m_colSelection.RemoveAt(n); - n--; - count--; - - colSelectionWasChanged = true; - - if (m_selectionMode == wxGrid::wxGridSelectCells) - { - if ( row > 0 ) - SelectBlockNoEvent(0, col, row - 1, col); - if ( row < m_grid->GetNumberRows() - 1 ) - SelectBlockNoEvent(row + 1, col, - m_grid->GetNumberRows() - 1, col); - } - } - } - } - - // Refresh the screen and send the event; according to m_selectionMode, - // we need to either update only the cell, or the whole row/column. - wxRect r; - if ( m_selectionMode == wxGrid::wxGridSelectCells ) - { if ( !m_grid->GetBatchCount() ) { - m_grid->RefreshBlock(row, col, row, col); + m_grid->RefreshBlock(refBlock.GetTopRow(), refBlock.GetLeftCol(), + refBlock.GetBottomRow(), refBlock.GetRightCol()); } - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - wxGridCellCoords( row, col ), - wxGridCellCoords( row, col ), - false, - kbd ); - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); - } - else // rows/columns selection mode - { - if ( m_selectionMode != wxGrid::wxGridSelectColumns && - rowSelectionWasChanged ) + if ( sendEvent ) { - int numCols = m_grid->GetNumberCols(); - for ( int colFrom = 0, colTo = 0; colTo <= numCols; ++colTo ) - { - if ( m_colSelection.Index(colTo) >= 0 || colTo == numCols ) - { - if ( colFrom < colTo ) - { - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(row, colFrom, row, colTo - 1); - } - - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - wxGridCellCoords( row, colFrom ), - wxGridCellCoords( row, colTo - 1 ), - false, - kbd ); - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); - } - - colFrom = colTo + 1; - } - } - } - - if ( m_selectionMode != wxGrid::wxGridSelectRows && - colSelectionWasChanged ) - { - int numRows = m_grid->GetNumberRows(); - for ( int rowFrom = 0, rowTo = 0; rowTo <= numRows; ++rowTo ) - { - if ( m_rowSelection.Index(rowTo) >= 0 || rowTo == numRows ) - { - if (rowFrom < rowTo) - { - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(rowFrom, col, rowTo - 1, col); - } - - wxGridRangeSelectEvent gridEvt( m_grid->GetId(), - wxEVT_GRID_RANGE_SELECT, - m_grid, - wxGridCellCoords( rowFrom, col ), - wxGridCellCoords( rowTo - 1, col ), - false, - kbd ); - m_grid->GetEventHandler()->ProcessEvent( gridEvt ); - } - - rowFrom = rowTo + 1; - } - } + wxGridRangeSelectEvent gridEvt(m_grid->GetId(), + wxEVT_GRID_RANGE_SELECT, + m_grid, + refBlock.GetTopLeft(), + refBlock.GetBottomRight(), + false, + kbd); + m_grid->GetEventHandler()->ProcessEvent(gridEvt); } } } @@ -882,33 +340,14 @@ void wxGridSelection::ClearSelection() wxRect r; wxGridCellCoords coords1, coords2; - // deselect all individual cells and update the screen - if ( m_selectionMode == wxGrid::wxGridSelectCells ) - { - while ( ( n = m_cellSelection.GetCount() ) > 0) - { - n--; - coords1 = m_cellSelection[n]; - m_cellSelection.RemoveAt(n); - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(coords1, coords1); - -#ifdef __WXMAC__ - m_grid->UpdateGridWindows(); -#endif - } - } - } - // deselect all blocks and update the screen - while ( ( n = m_blockSelectionTopLeft.GetCount() ) > 0) + while ( ( n = m_selection.size() ) > 0) { n--; - coords1 = m_blockSelectionTopLeft[n]; - coords2 = m_blockSelectionBottomRight[n]; - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); + const wxGridBlockCoords& block = m_selection[n]; + coords1 = block.GetTopLeft(); + coords2 = block.GetBottomRight(); + m_selection.erase(m_selection.begin() + n); if ( !m_grid->GetBatchCount() ) { m_grid->RefreshBlock(coords1, coords2); @@ -919,44 +358,6 @@ void wxGridSelection::ClearSelection() } } - // deselect all rows and update the screen - if ( m_selectionMode != wxGrid::wxGridSelectColumns ) - { - while ( ( n = m_rowSelection.GetCount() ) > 0) - { - n--; - int row = m_rowSelection[n]; - m_rowSelection.RemoveAt(n); - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(row, 0, row, m_grid->GetNumberCols() - 1); - -#ifdef __WXMAC__ - m_grid->UpdateGridWindows(); -#endif - } - } - } - - // deselect all columns and update the screen - if ( m_selectionMode != wxGrid::wxGridSelectRows ) - { - while ( ( n = m_colSelection.GetCount() ) > 0) - { - n--; - int col = m_colSelection[n]; - m_colSelection.RemoveAt(n); - if ( !m_grid->GetBatchCount() ) - { - m_grid->RefreshBlock(0, col, m_grid->GetNumberRows() - 1, col); - -#ifdef __WXMAC__ - m_grid->UpdateGridWindows(); -#endif - } - } - } - // One deselection event, indicating deselection of _all_ cells. // (No finer grained events for each of the smaller regions // deselected above!) @@ -975,54 +376,23 @@ void wxGridSelection::ClearSelection() void wxGridSelection::UpdateRows( size_t pos, int numRows ) { - size_t count = m_cellSelection.GetCount(); + size_t count = m_selection.size(); size_t n; - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords = m_cellSelection[n]; - wxCoord row = coords.GetRow(); - if ((size_t)row >= pos) - { - if (numRows > 0) - { - // If rows inserted, increase row counter where necessary - coords.SetRow(row + numRows); - } - else if (numRows < 0) - { - // If rows deleted ... - if ((size_t)row >= pos - numRows) - { - // ...either decrement row counter (if row still exists)... - coords.SetRow(row + numRows); - } - else - { - // ...or remove the attribute - m_cellSelection.RemoveAt(n); - n--; - count--; - } - } - } - } - count = m_blockSelectionTopLeft.GetCount(); for ( n = 0; n < count; n++ ) { - wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n]; - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - wxCoord row1 = coords1.GetRow(); - wxCoord row2 = coords2.GetRow(); + wxGridBlockCoords& block = m_selection[n]; + wxCoord row1 = block.GetTopRow(); + wxCoord row2 = block.GetBottomRow(); if ((size_t)row2 >= pos) { if (numRows > 0) { // If rows inserted, increase row counter where necessary - coords2.SetRow( row2 + numRows ); + block.SetBottomRow( row2 + numRows ); if ((size_t)row1 >= pos) - coords1.SetRow( row1 + numRows ); + block.SetTopRow( row1 + numRows ); } else if (numRows < 0) { @@ -1030,9 +400,9 @@ void wxGridSelection::UpdateRows( size_t pos, int numRows ) if ((size_t)row2 >= pos - numRows) { // ...either decrement row counter (if row still exists)... - coords2.SetRow( row2 + numRows ); + block.SetBottomRow( row2 + numRows ); if ((size_t)row1 >= pos) - coords1.SetRow( wxMax(row1 + numRows, (int)pos) ); + block.SetTopRow( wxMax(row1 + numRows, (int)pos) ); } else @@ -1040,101 +410,38 @@ void wxGridSelection::UpdateRows( size_t pos, int numRows ) if ((size_t)row1 >= pos) { // ...or remove the attribute - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); + m_selection.erase(m_selection.begin() + n); n--; count--; } else - coords2.SetRow( pos ); + block.SetBottomRow( pos ); } } } } - - count = m_rowSelection.GetCount(); - for ( n = 0; n < count; n++ ) - { - int rowOrCol_ = m_rowSelection[n]; - - if ((size_t) rowOrCol_ >= pos) - { - if ( numRows > 0 ) - { - m_rowSelection[n] += numRows; - } - else if ( numRows < 0 ) - { - if ((size_t)rowOrCol_ >= (pos - numRows)) - m_rowSelection[n] += numRows; - else - { - m_rowSelection.RemoveAt( n ); - n--; - count--; - } - } - } - } - // No need to touch selected columns, unless we removed _all_ - // rows, in this case, we remove all columns from the selection. - - if ( !m_grid->GetNumberRows() ) - m_colSelection.Clear(); } void wxGridSelection::UpdateCols( size_t pos, int numCols ) { - size_t count = m_cellSelection.GetCount(); + size_t count = m_selection.size(); size_t n; for ( n = 0; n < count; n++ ) { - wxGridCellCoords& coords = m_cellSelection[n]; - wxCoord col = coords.GetCol(); - if ((size_t)col >= pos) - { - if (numCols > 0) - { - // If rows inserted, increase row counter where necessary - coords.SetCol(col + numCols); - } - else if (numCols < 0) - { - // If rows deleted ... - if ((size_t)col >= pos - numCols) - { - // ...either decrement row counter (if row still exists)... - coords.SetCol(col + numCols); - } - else - { - // ...or remove the attribute - m_cellSelection.RemoveAt(n); - n--; - count--; - } - } - } - } - - count = m_blockSelectionTopLeft.GetCount(); - for ( n = 0; n < count; n++ ) - { - wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n]; - wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n]; - wxCoord col1 = coords1.GetCol(); - wxCoord col2 = coords2.GetCol(); + wxGridBlockCoords& block = m_selection[n]; + wxCoord col1 = block.GetLeftCol(); + wxCoord col2 = block.GetRightCol(); if ((size_t)col2 >= pos) { if (numCols > 0) { // If rows inserted, increase row counter where necessary - coords2.SetCol(col2 + numCols); + block.SetRightCol(col2 + numCols); if ((size_t)col1 >= pos) - coords1.SetCol(col1 + numCols); + block.SetLeftCol(col1 + numCols); } else if (numCols < 0) { @@ -1142,9 +449,9 @@ void wxGridSelection::UpdateCols( size_t pos, int numCols ) if ((size_t)col2 >= pos - numCols) { // ...either decrement col counter (if col still exists)... - coords2.SetCol(col2 + numCols); + block.SetRightCol(col2 + numCols); if ( (size_t) col1 >= pos) - coords1.SetCol( wxMax(col1 + numCols, (int)pos) ); + block.SetLeftCol( wxMax(col1 + numCols, (int)pos) ); } else @@ -1152,45 +459,133 @@ void wxGridSelection::UpdateCols( size_t pos, int numCols ) if ((size_t)col1 >= pos) { // ...or remove the attribute - m_blockSelectionTopLeft.RemoveAt(n); - m_blockSelectionBottomRight.RemoveAt(n); + m_selection.erase(m_selection.begin() + n); n--; count--; } else - coords2.SetCol(pos); + block.SetRightCol(pos); } } } } +} - count = m_colSelection.GetCount(); - for ( n = 0; n < count; n++ ) +wxGridCellCoordsArray wxGridSelection::GetCellSelection() const +{ + if ( m_selectionMode != wxGrid::wxGridSelectCells ) + return wxGridCellCoordsArray(); + + wxGridCellCoordsArray cells; + const size_t count = m_selection.size(); + cells.reserve(count); + for ( size_t n = 0; n < count; n++ ) { - int rowOrCol = m_colSelection[n]; - - if ((size_t)rowOrCol >= pos) + const wxGridBlockCoords& block = m_selection[n]; + if ( block.GetTopRow() == block.GetBottomRow() && + block.GetLeftCol() == block.GetRightCol() ) { - if ( numCols > 0 ) - m_colSelection[n] += numCols; - else if ( numCols < 0 ) + cells.push_back(block.GetTopLeft()); + } + } + return cells; +} + +wxGridCellCoordsArray wxGridSelection::GetBlockSelectionTopLeft() const +{ + // return blocks only in wxGridSelectCells selection mode + if ( m_selectionMode != wxGrid::wxGridSelectCells ) + return wxGridCellCoordsArray(); + + wxGridCellCoordsArray coords; + const size_t count = m_selection.size(); + coords.reserve(count); + for ( size_t n = 0; n < count; n++ ) + { + coords.push_back(m_selection[n].GetTopLeft()); + } + return coords; +} + +wxGridCellCoordsArray wxGridSelection::GetBlockSelectionBottomRight() const +{ + if ( m_selectionMode != wxGrid::wxGridSelectCells ) + return wxGridCellCoordsArray(); + + wxGridCellCoordsArray coords; + const size_t count = m_selection.size(); + coords.reserve(count); + for ( size_t n = 0; n < count; n++ ) + { + coords.push_back(m_selection[n].GetBottomRight()); + } + return coords; +} + +// For compatibility with the existing code, try reconstructing the selected +// rows/columns from the selected blocks we store internally. Of course, this +// only works well in the corresponding selection mode in which the user can +// only select the entire lines in the first place, as otherwise it's difficult to +// efficiently determine that a line is selected because all of its cells +// were selected one by one. But this should work well enough in practice and +// is, anyhow, the best we can do. +wxArrayInt wxGridSelection::GetRowSelection() const +{ + if ( m_selectionMode == wxGrid::wxGridSelectColumns ) + return wxArrayInt(); + + wxIntSortedArray uniqueRows(&CompareInts); + const size_t count = m_selection.size(); + for ( size_t n = 0; n < count; ++n ) + { + const wxGridBlockCoords& block = m_selection[n]; + if ( block.GetLeftCol() == 0 && + block.GetRightCol() == m_grid->GetNumberCols() - 1 ) + { + for ( int r = block.GetTopRow(); r <= block.GetBottomRow(); ++r ) { - if ((size_t)rowOrCol >= (pos - numCols)) - m_colSelection[n] += numCols; - else - { - m_colSelection.RemoveAt( n ); - n--; - count--; - } + uniqueRows.Add(r); } } } - // No need to touch selected rows, unless we removed _all_ - // columns, in this case, we remove all rows from the selection. - if ( !m_grid->GetNumberCols() ) - m_rowSelection.Clear(); + wxArrayInt result; + result.reserve(uniqueRows.size()); + for( size_t i = 0; i < uniqueRows.size(); ++i ) + { + result.push_back(uniqueRows[i]); + } + return result; +} + +// See comments for GetRowSelection(). +wxArrayInt wxGridSelection::GetColSelection() const +{ + if ( m_selectionMode == wxGrid::wxGridSelectRows ) + return wxArrayInt(); + + wxIntSortedArray uniqueRows(&CompareInts); + const size_t count = m_selection.size(); + for ( size_t n = 0; n < count; ++n ) + { + const wxGridBlockCoords& block = m_selection[n]; + if ( block.GetTopRow() == 0 && + block.GetBottomRow() == m_grid->GetNumberRows() - 1 ) + { + for ( int c = block.GetLeftCol(); c <= block.GetRightCol(); ++c ) + { + uniqueRows.Add(c); + } + } + } + + wxArrayInt result; + result.reserve(uniqueRows.size()); + for( size_t i = 0; i < uniqueRows.size(); ++i ) + { + result.push_back(uniqueRows[i]); + } + return result; } int wxGridSelection::BlockContain( int topRow1, int leftCol1, @@ -1211,4 +606,66 @@ int wxGridSelection::BlockContain( int topRow1, int leftCol1, return 0; } +void +wxGridSelection::Select(const wxGridBlockCoords& block, + const wxKeyboardState& kbd, bool sendEvent) +{ + if (m_grid->GetNumberRows() == 0 || m_grid->GetNumberCols() == 0) + return; + + MergeOrAddBlock(m_selection, block); + + // Update View: + if ( !m_grid->GetBatchCount() ) + { + m_grid->RefreshBlock(block.GetTopLeft(), block.GetBottomRight()); + } + + // Send Event, if not disabled. + if ( sendEvent ) + { + wxGridRangeSelectEvent gridEvt( m_grid->GetId(), + wxEVT_GRID_RANGE_SELECT, + m_grid, + block.GetTopLeft(), + block.GetBottomRight(), + true, + kbd); + m_grid->GetEventHandler()->ProcessEvent( gridEvt ); + } +} + +void wxGridSelection::MergeOrAddBlock(wxVectorGridBlockCoords& blocks, + const wxGridBlockCoords& newBlock) +{ + // If a block containing the selection is already selected, return, + // if a block contained in the selection is found, remove it. + + size_t count = blocks.size(); + for ( size_t n = 0; n < count; n++ ) + { + const wxGridBlockCoords& block = blocks[n]; + + switch ( BlockContain(block.GetTopRow(), block.GetLeftCol(), + block.GetBottomRow(), block.GetRightCol(), + newBlock.GetTopRow(), newBlock.GetLeftCol(), + newBlock.GetBottomRow(), newBlock.GetRightCol()) ) + { + case 1: + return; + + case -1: + blocks.erase(blocks.begin() + n); + n--; + count--; + break; + + default: + break; + } + } + + blocks.push_back(newBlock); +} + #endif