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