package com.vaadin.addon.spreadsheet.client; /* * #%L * Vaadin Spreadsheet * %% * Copyright (C) 2013 - 2015 Vaadin Ltd * %% * This program is available under Commercial Vaadin Add-On License 3.0 * (CVALv3). * * See the file license.html distributed with this software for more * information about licensing. * * You should have received a copy of the CVALv3 along with this program. * If not, see <http://vaadin.com/license/cval-3>. * #L% */ import com.google.gwt.user.client.ui.Widget; /** * Class that handles finer details of cell selection inside the Spreadsheet. * * @author Thomas Mattsson / Vaadin Ltd. */ public class SelectionHandler { private final SheetWidget sheetWidget; private final SpreadsheetWidget spreadsheet; private int colBeforeMergedCell; private int rowBeforeMergedCell; public SelectionHandler(SpreadsheetWidget spreadsheet, SheetWidget widget) { sheetWidget = widget; this.spreadsheet = spreadsheet; } public void moveSelectionDown(boolean discardSelection) { final int leftCol = sheetWidget.getSelectionLeftCol(); final int rightCol = sheetWidget.getSelectionRightCol(); final int topRow = sheetWidget.getSelectionTopRow(); final int bottomRow = sheetWidget.getSelectionBottomRow(); int col = sheetWidget.getSelectedCellColumn(); int row = sheetWidget.getSelectedCellRow(); // if the old selected cell was a merged cell, it changes the actual // selected cell MergedRegion oldRegion = spreadsheet.getMergedRegion(col, row); if (oldRegion != null && colBeforeMergedCell != 0) { col = colBeforeMergedCell; row = oldRegion.row2; } row++; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row < spreadsheet.getMaxRows()) { row++; } if (!discardSelection && (leftCol != rightCol || topRow != bottomRow) && (oldRegion == null || leftCol != oldRegion.col1 || rightCol != oldRegion.col2 || topRow != oldRegion.row1 || bottomRow != oldRegion.row2)) { // move the selected cell inside the selection if (row > bottomRow) { // move highest and right row = topRow; // if the row on top is hidden, skip it while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row < bottomRow) { row++; } col++; // if the column on right is hidden, skip it while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col) && col <= rightCol) { col++; } if (col > rightCol) { // move to left col = leftCol; } while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col) && col <= rightCol) { col++; } } checkNewSelectionInMergedRegion(col, row); } else { if (row <= spreadsheet.getMaxRows()) { // if the new selected cell is a merged cell checkSelectionInMergedRegion(col, row); } } } public void selectCellRange(int selectedCellColumn, int selectedCellRow, int firstColumn, int lastColumn, int firstRow, int lastRow, String value, boolean formula, boolean locked, boolean scroll) { spreadsheet.updateSelectedCellValues(selectedCellColumn, selectedCellRow); if (!sheetWidget.isCoherentSelection()) { sheetWidget.setCoherentSelection(true); } if (!sheetWidget.isSelectionRangeOutlineVisible()) { sheetWidget.setSelectionRangeOutlineVisible(true); sheetWidget.clearSelectedCellStyle(); } final int oldSelectedCellCol = sheetWidget.getSelectedCellColumn(); final int oldSelectedCellRow = sheetWidget.getSelectedCellRow(); if (oldSelectedCellCol != selectedCellColumn || oldSelectedCellRow != selectedCellRow) { sheetWidget.setSelectedCell(selectedCellColumn, selectedCellRow); newSelectedCellSet(); } sheetWidget.updateSelectionOutline(firstColumn, lastColumn, firstRow, lastRow); sheetWidget.updateSelectedCellStyles(firstColumn, lastColumn, firstRow, lastRow, true); if (scroll && !sheetWidget.isAreaCompletelyVisible(firstColumn, lastColumn, firstRow, lastRow)) { sheetWidget.scrollAreaIntoView(firstColumn, lastColumn, firstRow, lastRow); } sheetWidget.focusSheet(); } public void selectCell(int col, int row, String value, boolean formula, boolean locked, boolean initialSelection) { if (spreadsheet.customCellEditorDisplayed) { spreadsheet.customCellEditorDisplayed = false; sheetWidget.removeCustomCellEditor(); } spreadsheet.cellLocked = locked; sheetWidget.setSelectedCell(col, row); newSelectedCellSet(); if (!sheetWidget.isCoherentSelection()) { sheetWidget.setCoherentSelection(true); } if (!sheetWidget.isSelectionRangeOutlineVisible()) { sheetWidget.setSelectionRangeOutlineVisible(true); sheetWidget.clearSelectedCellStyle(); } sheetWidget.updateSelectionOutline(col, col, row, row); sheetWidget.updateSelectedCellStyles(col, col, row, row, true); if (formula) { spreadsheet.formulaBarWidget.setCellFormulaValue(value); } else { spreadsheet.formulaBarWidget.setCellPlainValue(value); } spreadsheet.formulaBarWidget.setFormulaFieldEnabled(!locked); spreadsheet.formulaBarWidget.setSelectedCellAddress(spreadsheet .createCellAddress(col, row)); // scroll the cell into view if (!sheetWidget.isSelectedCellCompletelyVisible()) { sheetWidget.scrollCellIntoView(col, row); } if (!initialSelection) { sheetWidget.focusSheet(); } } public void newSelectedCellSet() { if (spreadsheet.customCellEditorDisplayed) { spreadsheet.customCellEditorDisplayed = false; sheetWidget.removeCustomCellEditor(); } if (!sheetWidget.isSelectedCellCustomized() && !spreadsheet.cellLocked && spreadsheet.customEditorFactory != null && spreadsheet.customEditorFactory.hasCustomEditor(sheetWidget .getSelectedCellKey())) { Widget customEditor = spreadsheet.customEditorFactory .getCustomEditor(sheetWidget.getSelectedCellKey()); if (customEditor != null) { spreadsheet.customCellEditorDisplayed = true; spreadsheet.formulaBarWidget.setFormulaFieldEnabled(false); sheetWidget.displayCustomCellEditor(customEditor); } } } public void moveSelectionUp(boolean discardSelection) { final int leftCol = sheetWidget.getSelectionLeftCol(); final int rightCol = sheetWidget.getSelectionRightCol(); final int topRow = sheetWidget.getSelectionTopRow(); final int bottomRow = sheetWidget.getSelectionBottomRow(); int col = sheetWidget.getSelectedCellColumn(); int row = sheetWidget.getSelectedCellRow(); // if the old selected cell was a merged cell, it changes the actual // selected cell MergedRegion oldRegion = spreadsheet.getMergedRegion(col, row); if (oldRegion != null && colBeforeMergedCell != 0) { col = colBeforeMergedCell; row = oldRegion.row1; } row--; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row > 1) { row--; } if (!discardSelection && (leftCol != rightCol || topRow != bottomRow) && (oldRegion == null || leftCol != oldRegion.col1 || rightCol != oldRegion.col2 || topRow != oldRegion.row1 || bottomRow != oldRegion.row2)) { // move the selected cell inside the selection if (row < topRow) { // go to bottom and left row = bottomRow; // if row on bottom is hidden, skip it while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row > topRow) { row--; } col--; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col) && col >= leftCol) { col--; } if (col < leftCol) { // go to right most col = rightCol; } while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col) && col >= leftCol) { col--; } } checkNewSelectionInMergedRegion(col, row); } else { if (row > 0) { checkSelectionInMergedRegion(col, row); } } } protected void onCellSelectedWithKeyboard(int column, int row, String value, MergedRegion region) { spreadsheet.doCommitIfEditing(); if (!sheetWidget.isCoherentSelection()) { sheetWidget.setCoherentSelection(true); } if (!sheetWidget.isSelectionRangeOutlineVisible()) { sheetWidget.setSelectionRangeOutlineVisible(true); sheetWidget.clearSelectedCellStyle(); } sheetWidget.setSelectedCell(column, row); sheetWidget.updateSelectionOutline(column, column, row, row); if (region != null) { sheetWidget.updateSelectedCellStyles(column, region.col2, row, region.row2, true); } else { sheetWidget .updateSelectedCellStyles(column, column, row, row, true); } newSelectedCellSet(); spreadsheet.updateSelectedCellValues(column, row); spreadsheet.spreadsheetHandler.cellSelected(row, column, true); spreadsheet.startDelayedSendingTimer(); } public void increaseHorizontalSelection(boolean right) { // TODO refactor to smaller pieces int topRow = sheetWidget.getSelectionTopRow(); int leftCol = sheetWidget.getSelectionLeftCol(); final int oldLeftCol = leftCol; int rightCol = sheetWidget.getSelectionRightCol(); final int oldRightCol = rightCol; int bottomRow = sheetWidget.getSelectionBottomRow(); int selectedCellColumn = sheetWidget.getSelectedCellColumn(); final int selectedCellRow = sheetWidget.getSelectedCellRow(); MergedRegion region = spreadsheet.mergedRegionContainer .getMergedRegionStartingFrom(selectedCellColumn, selectedCellRow); boolean actOnLeftSide = false; if (sheetWidget.isCoherentSelection()) { // the selection outline is the "correct", even with merged cells, // as with a merged cell the selected cell doesn't take the merged // edge into account. if (region != null && (right && region.col1 != leftCol || !right && region.col2 == rightCol)) { selectedCellColumn = region.col2; } MergedRegion selection = null; if (selectedCellColumn == leftCol) { if (right && rightCol + 1 <= spreadsheet.getMaxColumns()) { // increase to right rightCol++; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(rightCol) && rightCol < spreadsheet.getMaxColumns()) { rightCol++; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } else if (!right) { if (rightCol != leftCol) { // Decrease from right rightCol--; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(rightCol) && (rightCol) > leftCol) { rightCol--; } selection = findDecreasingSelection(topRow, bottomRow, leftCol, rightCol); } else if (leftCol - 1 > 0) { // Increase to left actOnLeftSide = true; leftCol--; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(leftCol) && leftCol > 1) { leftCol--; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } } else if (selectedCellColumn == rightCol) { if (right) { if (rightCol != leftCol) { // Decrease from left actOnLeftSide = true; leftCol++; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(leftCol) && leftCol < rightCol) { leftCol++; } selection = findDecreasingSelection(topRow, bottomRow, leftCol, rightCol); } else if (rightCol + 1 <= spreadsheet.getMaxColumns()) { // increase to right rightCol++; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(rightCol) && rightCol < spreadsheet.getMaxColumns()) { rightCol++; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } else if (!right && leftCol - 1 > 0) { // Increase to left actOnLeftSide = true; leftCol--; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(leftCol) && leftCol > 1) { leftCol--; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } else { if (right) { // Increase to right if (rightCol + 1 <= spreadsheet.getMaxColumns()) { rightCol++; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(rightCol) && rightCol < spreadsheet.getMaxColumns()) { rightCol++; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } else { // Increase to left actOnLeftSide = true; if (leftCol - 1 > 0) { leftCol--; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(leftCol) && leftCol > 1) { leftCol--; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } } if (selection == null) { return; } sheetWidget.updateSelectionOutline(selection.col1, selection.col2, selection.row1, selection.row2); sheetWidget.replaceAsSelectedCells(selection.col1, selection.col2, selection.row1, selection.row2); sheetWidget.replaceHeadersAsSelected(selection.row1, selection.row2, selection.col1, selection.col2); sheetWidget.scrollAreaIntoViewHorizontally(selection.col1, selection.col2, actOnLeftSide); } else { // previous selection not coherent // discard the old selection and start from previously selected cell int row2; int col2; if (region != null) { row2 = region.row2; col2 = region.col2; } else { row2 = selectedCellRow; col2 = selectedCellColumn; } if (right) { col2++; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col2) && col2 < spreadsheet.getMaxColumns()) { col2++; } } else { selectedCellColumn--; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(selectedCellColumn) && selectedCellColumn > 1) { selectedCellColumn--; } } if (selectedCellColumn > 0 && col2 < spreadsheet.getMaxColumns()) { MergedRegion selection = MergedRegionUtil .findIncreasingSelection( spreadsheet.mergedRegionContainer, selectedCellRow, row2, selectedCellColumn, col2); if (selection != null) { // sheetWidget.clearCellRangeStyles(); sheetWidget.setCoherentSelection(true); sheetWidget.setSelectionRangeOutlineVisible(true); sheetWidget.clearSelectedCellStyle(); sheetWidget.updateSelectionOutline(selection.col1, selection.col2, selection.row1, selection.row2); sheetWidget.updateSelectedCellStyles(selection.col1, selection.col2, selection.row1, selection.row2, true); } } // scroll area into view sheetWidget.scrollSelectionAreaIntoView(); } // update action handler if (oldLeftCol != sheetWidget.getSelectionLeftCol() || oldRightCol != sheetWidget.getSelectionRightCol() || topRow != sheetWidget.getSelectionTopRow() || bottomRow != sheetWidget.getSelectionBottomRow()) { spreadsheet.spreadsheetHandler.cellRangeSelected( sheetWidget.getSelectionTopRow(), sheetWidget.getSelectionLeftCol(), sheetWidget.getSelectionBottomRow(), sheetWidget.getSelectionRightCol()); spreadsheet.startDelayedSendingTimer(); } } /** * Goes through the given selection and checks that the cells on the edges * of the selection are not in "the beginning / middle / end" of a merged * cell. Returns the correct decreased selection, after taking the merged * cells into account. * * Parameters 1-based. * * @param topRow * @param bottomRow * @param leftColumn * @param rightColumn * @return */ protected MergedRegion findDecreasingSelection(int topRow, int bottomRow, int leftColumn, int rightColumn) { if (topRow == bottomRow && leftColumn == rightColumn) { MergedRegion mergedRegion = spreadsheet.getMergedRegion(leftColumn, topRow); if (mergedRegion == null) { mergedRegion = new MergedRegion(); mergedRegion.col1 = leftColumn; mergedRegion.col2 = rightColumn; mergedRegion.row1 = topRow; mergedRegion.row2 = bottomRow; } return mergedRegion; } else { MergedRegion merged = spreadsheet.getMergedRegionStartingFrom( leftColumn, topRow); if (merged != null && merged.col2 >= rightColumn && merged.row2 >= bottomRow) { return merged; } } int selectedCellColumn = sheetWidget.getSelectedCellColumn(); int selectedCellRow = sheetWidget.getSelectedCellRow(); if (selectedCellColumn < leftColumn || selectedCellColumn > rightColumn || selectedCellRow < topRow || selectedCellRow > bottomRow) { return spreadsheet.getMergedRegion(selectedCellColumn, sheetWidget.getSelectedCellRow()); } boolean trouble = false; int i = leftColumn; // go through top edge while (i <= rightColumn) { MergedRegion region = spreadsheet.getMergedRegion(i, topRow); if (region != null) { i = region.col2 + 1; if (topRow > region.row1) { // check if the cell in top row is in middle or end of a // merged cell -> decrease more if it is trouble = true; if (topRow < bottomRow) { if (region.row2 > bottomRow) { topRow = region.row2 + 1; } else { topRow = bottomRow; } i = leftColumn; } else { if (selectedCellColumn < region.col1) { rightColumn = region.col1 - 1; } else if (selectedCellColumn > region.col2) { leftColumn = region.col2 + 1; } else { leftColumn = region.col1; rightColumn = region.col2; break; } } } } else { i++; } } if (topRow > bottomRow) { topRow = bottomRow; } // go through right edge i = topRow; while (i <= bottomRow) { MergedRegion region = spreadsheet.getMergedRegion(rightColumn, i); if (region != null) { i = region.row2 + 1; if (rightColumn < region.col2) { trouble = true; if (rightColumn > leftColumn) { if (region.col1 > leftColumn) { rightColumn = region.col1 - 1; } else { rightColumn = leftColumn; } i = topRow; } else { if (selectedCellRow < region.row1) { bottomRow = region.row1 - 1; } else if (selectedCellRow > region.row2) { topRow = region.row2 + 1; } else { // selected cell row is inside the region topRow = region.row1; bottomRow = region.row2; break; } } } } else { i++; } } if (rightColumn < leftColumn) { rightColumn = leftColumn; } // go through bottom edge i = leftColumn; while (i <= rightColumn) { MergedRegion region = spreadsheet.getMergedRegion(i, bottomRow); if (region != null) { i = region.col2 + 1; if (bottomRow < region.row2) { trouble = true; if (bottomRow > topRow) { if (topRow < region.row1) { bottomRow = region.row1 - 1; } else { bottomRow = topRow; } i = leftColumn; } else { if (selectedCellColumn < region.col1) { rightColumn = region.col1 - 1; } else if (selectedCellColumn > region.col2) { leftColumn = region.col2 + 1; } else { rightColumn = region.col1; leftColumn = region.col2; break; } } } } else { i++; } } if (bottomRow < topRow) { bottomRow = topRow; } // go through left edge i = topRow; while (i <= bottomRow) { MergedRegion region = spreadsheet.getMergedRegion(leftColumn, i); if (region != null) { i = region.row2 + 1; if (leftColumn > region.col1) { trouble = true; if (leftColumn < rightColumn) { if (rightColumn > region.col2) { leftColumn = region.col2 + 1; } else { leftColumn = rightColumn; } i = topRow; } else { if (selectedCellRow < region.row1) { bottomRow = region.row1 - 1; } else if (selectedCellRow > region.row2) { topRow = region.row2 + 1; } else { topRow = region.row1; bottomRow = region.row2; break; } } } } else { i++; } } if (leftColumn > rightColumn) { leftColumn = rightColumn; } if (trouble) { return findDecreasingSelection(topRow, bottomRow, leftColumn, rightColumn); } else if (topRow == bottomRow && leftColumn == rightColumn) { MergedRegion mergedRegion = spreadsheet.getMergedRegion(leftColumn, topRow); if (mergedRegion == null) { mergedRegion = new MergedRegion(); mergedRegion.col1 = leftColumn; mergedRegion.col2 = rightColumn; mergedRegion.row1 = topRow; mergedRegion.row2 = bottomRow; } return mergedRegion; } else { MergedRegion merged = spreadsheet.getMergedRegionStartingFrom( leftColumn, topRow); if (merged != null && merged.col2 >= rightColumn && merged.row2 >= bottomRow) { return merged; } } MergedRegion result = new MergedRegion(); result.col1 = leftColumn; result.col2 = rightColumn; result.row1 = topRow; result.row2 = bottomRow; return result; } public void increaseVerticalSelection(boolean down) { // TODO refactor to smaller pieces int topRow = sheetWidget.getSelectionTopRow(); int oldTopRow = topRow; final int leftCol = sheetWidget.getSelectionLeftCol(); int bottomRow = sheetWidget.getSelectionBottomRow(); int oldBottomRow = bottomRow; final int rightCol = sheetWidget.getSelectionRightCol(); int selectedCellRow = sheetWidget.getSelectedCellRow(); final int selectedCellColumn = sheetWidget.getSelectedCellColumn(); MergedRegion region = spreadsheet.getMergedRegionStartingFrom( selectedCellColumn, selectedCellRow); boolean actOnTopEdge = false; if (sheetWidget.isCoherentSelection()) { if (region != null && (down && region.row1 != topRow || !down && region.row2 == bottomRow)) { selectedCellRow = region.row2; } MergedRegion selection = null; if (selectedCellRow == topRow) { if (down && bottomRow + 1 <= spreadsheet.getMaxRows()) { // increase selection down bottomRow++; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(bottomRow) && bottomRow < spreadsheet.getMaxRows()) { bottomRow++; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } else if (!down) { if (topRow != bottomRow) { // Decrease from bottom bottomRow--; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes .contains(bottomRow) && bottomRow > topRow) { bottomRow--; } selection = findDecreasingSelection(topRow, bottomRow, leftCol, rightCol); } else if (topRow - 1 > 0) { // Increase up actOnTopEdge = true; topRow--; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes .contains(topRow) && topRow > 1) { topRow--; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } } else if (selectedCellRow == bottomRow) { if (down) { if (topRow != bottomRow) { // Decrease from top actOnTopEdge = true; topRow++; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes .contains(topRow) && topRow < bottomRow) { topRow++; } selection = findDecreasingSelection(topRow, bottomRow, leftCol, rightCol); } else if (bottomRow + 1 <= spreadsheet.getMaxRows()) { // increase selection down bottomRow++; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes .contains(bottomRow) && bottomRow < spreadsheet.getMaxRows()) { bottomRow++; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } else if (!down && topRow - 1 > 0) { // Increase up actOnTopEdge = true; topRow--; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(topRow) && topRow > 1) { topRow--; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } else { // Increase the selection on the desired direction if (down) { if (bottomRow + 1 <= spreadsheet.getMaxRows()) { bottomRow++; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes .contains(bottomRow) && bottomRow < spreadsheet.getMaxRows()) { bottomRow++; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } else { actOnTopEdge = true; if (topRow - 1 > 0) { topRow--; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes .contains(topRow) && topRow > 1) { topRow--; } selection = MergedRegionUtil.findIncreasingSelection( spreadsheet.mergedRegionContainer, topRow, bottomRow, leftCol, rightCol); } } } if (selection == null) { return; } sheetWidget.updateSelectionOutline(selection.col1, selection.col2, selection.row1, selection.row2); sheetWidget.replaceAsSelectedCells(selection.col1, selection.col2, selection.row1, selection.row2); sheetWidget.replaceHeadersAsSelected(selection.row1, selection.row2, selection.col1, selection.col2); sheetWidget.scrollAreaIntoViewVertically(selection.row1, selection.row2, actOnTopEdge); } else { // previous selection not coherent // discard the old selection and start from previously selected cell int row2; int col2; if (region != null) { row2 = region.row2; col2 = region.col2; } else { row2 = selectedCellRow; col2 = selectedCellColumn; } if (down) { row2++; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row2) && row2 < spreadsheet.getMaxRows()) { row2++; } } else { selectedCellRow--; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes .contains(selectedCellRow) && selectedCellRow > 1) { selectedCellRow--; } } if (selectedCellRow > 0 && row2 <= spreadsheet.getMaxRows()) { MergedRegion selection = MergedRegionUtil .findIncreasingSelection( spreadsheet.mergedRegionContainer, selectedCellRow, row2, selectedCellColumn, col2); if (selection != null) { sheetWidget.setCoherentSelection(true); sheetWidget.setSelectionRangeOutlineVisible(true); sheetWidget.clearSelectedCellStyle(); sheetWidget.updateSelectionOutline(selection.col1, selection.col2, selection.row1, selection.row2); sheetWidget.updateSelectedCellStyles(selection.col1, selection.col2, selection.row1, selection.row2, true); } } // scroll area into view sheetWidget.scrollSelectionAreaIntoView(); } // update action handler if (leftCol != sheetWidget.getSelectionLeftCol() || rightCol != sheetWidget.getSelectionRightCol() || oldTopRow != sheetWidget.getSelectionTopRow() || oldBottomRow != sheetWidget.getSelectionBottomRow()) { spreadsheet.spreadsheetHandler.cellRangeSelected( sheetWidget.getSelectionTopRow(), sheetWidget.getSelectionLeftCol(), sheetWidget.getSelectionBottomRow(), sheetWidget.getSelectionRightCol()); spreadsheet.startDelayedSendingTimer(); } } public void moveSelectionRight(boolean discardSelection) { final int leftCol = sheetWidget.getSelectionLeftCol(); final int rightCol = sheetWidget.getSelectionRightCol(); final int topRow = sheetWidget.getSelectionTopRow(); final int bottomRow = sheetWidget.getSelectionBottomRow(); int col = sheetWidget.getSelectedCellColumn(); int row = sheetWidget.getSelectedCellRow(); // if the old selected cell was a merged cell, it changes the actual // selected cell MergedRegion oldRegion = spreadsheet.getMergedRegion(col, row); if (oldRegion != null && rowBeforeMergedCell != 0) { col = oldRegion.col2; row = rowBeforeMergedCell; } col++; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col) && col < spreadsheet.getMaxColumns()) { col++; } if (!discardSelection && (leftCol != rightCol || topRow != bottomRow) && (oldRegion == null || leftCol != oldRegion.col1 || rightCol != oldRegion.col2 || topRow != oldRegion.row1 || bottomRow != oldRegion.row2)) { // move the selected cell inside the selection if (col > rightCol) { // move to leftmost and down col = leftCol; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes .contains(new Integer(col)) && col <= rightCol) { col++; } row++; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row <= bottomRow) { row++; } if (row > bottomRow) { // move to top row = topRow; } while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row <= bottomRow) { row++; } } checkNewSelectionInMergedRegion(col, row); } else { if (col <= spreadsheet.getMaxColumns()) { checkSelectionInMergedRegion(col, row); } } } public void moveSelectionLeft(boolean discardSelection) { final int leftCol = sheetWidget.getSelectionLeftCol(); final int rightCol = sheetWidget.getSelectionRightCol(); final int topRow = sheetWidget.getSelectionTopRow(); final int bottomRow = sheetWidget.getSelectionBottomRow(); int col = sheetWidget.getSelectedCellColumn(); int row = sheetWidget.getSelectedCellRow(); // if the old selected cell was a merged cell, it changes the actual // selected cell MergedRegion oldRegion = spreadsheet.getMergedRegion(col, row); if (oldRegion != null && rowBeforeMergedCell != 0) { col = oldRegion.col1; row = rowBeforeMergedCell; } col--; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col) && col > 0) { col--; } if (!discardSelection && (leftCol != rightCol || topRow != bottomRow) && (oldRegion == null || leftCol != oldRegion.col1 || rightCol != oldRegion.col2 || topRow != oldRegion.row1 || bottomRow != oldRegion.row2)) { // move the selected cell inside the selection if (col < leftCol) { // move to right most and up col = rightCol; while (spreadsheet.hiddenColumnIndexes != null && spreadsheet.hiddenColumnIndexes.contains(col) && col >= leftCol) { col--; } row--; while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row >= topRow) { row--; } if (row < topRow) { // go to bottom row = bottomRow; } while (spreadsheet.hiddenRowIndexes != null && spreadsheet.hiddenRowIndexes.contains(row) && row >= topRow) { row--; } } // if the new selected cell is a merged cell checkNewSelectionInMergedRegion(col, row); } else { if (col > 0) { checkSelectionInMergedRegion(col, row); } } } /** * Same as {@link #checkSelectionInMergedRegion(int, int)}, but discards old * selection in favor of the given cell. */ protected void checkNewSelectionInMergedRegion(int col, int row) { MergedRegion region = spreadsheet.getMergedRegion(col, row); if (region != null) { colBeforeMergedCell = col; rowBeforeMergedCell = row; col = region.col1; row = region.row1; } else { colBeforeMergedCell = 0; rowBeforeMergedCell = 0; } // TODO check if order really matters here, can we call the normal // version and just run the extra methods after? sheetWidget.swapSelectedCellInsideSelection(col, row); sheetWidget.scrollCellIntoView(col, row); spreadsheet.updateSelectedCellValues(col, row); newSelectedCellSet(); spreadsheet.spreadsheetHandler.cellSelected(row, col, false); spreadsheet.startDelayedSendingTimer(); } /** * Same as {@link #checkNewSelectionInMergedRegion(int, int)}, but retains * old selection in addition to the new cell. */ protected void checkSelectionInMergedRegion(int col, int row) { MergedRegion region = spreadsheet.getMergedRegion(col, row); if (region != null) { colBeforeMergedCell = col; rowBeforeMergedCell = row; col = region.col1; row = region.row1; } else { colBeforeMergedCell = 0; rowBeforeMergedCell = 0; } sheetWidget.scrollCellIntoView(col, row); onCellSelectedWithKeyboard(col, row, sheetWidget.getCellValue(col, row), region); } public void clearBeforeMergeCells() { colBeforeMergedCell = 0; rowBeforeMergedCell = 0; } public void setColBeforeMergedCell(int c) { colBeforeMergedCell = c; } public void setRowBeforeMergedCell(int r) { rowBeforeMergedCell = r; } }