| /******************************************************************************* |
| * Copyright (c) 2012, 2020 Original authors and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Original authors and others - initial API and implementation |
| * Jonas Hugo <Jonas.Hugo@jeppesen.com>, |
| * Markus Wahl <Markus.Wahl@jeppesen.com> - Delegate markers to model if |
| * model is an IMarkerSelectionModel. Add getters and setters for marker fields |
| * neal zhang <nujiah001@126.com> - change some methods and fields visibility |
| * Dirk Fauth <dirk.fauth@googlemail.com> - Bug 446275, 453851 |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.selection; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.stream.IntStream; |
| |
| import org.eclipse.collections.api.list.primitive.MutableIntList; |
| import org.eclipse.collections.impl.factory.primitive.IntLists; |
| import org.eclipse.nebula.widgets.nattable.command.ILayerCommand; |
| import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate; |
| import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinateComparator; |
| import org.eclipse.nebula.widgets.nattable.coordinate.Range; |
| import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.edit.command.EditSelectionCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.fillhandle.FillHandleLayerPainter; |
| import org.eclipse.nebula.widgets.nattable.fillhandle.action.FillHandleDragMode; |
| import org.eclipse.nebula.widgets.nattable.grid.command.InitializeAutoResizeColumnsCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.grid.command.InitializeAutoResizeRowsCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.hideshow.command.ColumnHideCommand; |
| import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiColumnHideCommand; |
| import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiRowHideCommand; |
| import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommand; |
| import org.eclipse.nebula.widgets.nattable.hideshow.command.RowPositionHideCommand; |
| import org.eclipse.nebula.widgets.nattable.layer.AbstractIndexLayerTransform; |
| import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; |
| import org.eclipse.nebula.widgets.nattable.layer.LabelStack; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; |
| import org.eclipse.nebula.widgets.nattable.resize.command.ColumnResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.resize.command.MultiColumnResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.resize.command.MultiRowResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.resize.command.RowResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.search.command.SearchGridCellsCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.selection.command.ClearAllSelectionsCommand; |
| import org.eclipse.nebula.widgets.nattable.selection.command.SelectAllCommand; |
| import org.eclipse.nebula.widgets.nattable.selection.config.DefaultSelectionLayerConfiguration; |
| import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent; |
| import org.eclipse.nebula.widgets.nattable.style.DisplayMode; |
| import org.eclipse.nebula.widgets.nattable.style.SelectionStyleLabels; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| |
| /** |
| * Enables selection of column, rows, cells etc. on the table. Also responds to |
| * UI bindings by changing the current selection. Internally it uses the |
| * {@link ISelectionModel} to track the selection state. |
| * |
| * @see DefaultSelectionLayerConfiguration |
| * @see MoveDirectionEnum |
| */ |
| public class SelectionLayer extends AbstractIndexLayerTransform { |
| |
| public static final int MOVE_ALL = -1; |
| public static final int NO_SELECTION = -1; |
| |
| public enum MoveDirectionEnum { |
| UP, DOWN, LEFT, RIGHT, NONE; |
| } |
| |
| protected ISelectionModel selectionModel; |
| protected IUniqueIndexLayer underlyingLayer; |
| protected final PositionCoordinate lastSelectedCell; |
| protected final PositionCoordinate selectionAnchor; |
| protected Rectangle lastSelectedRegion; |
| |
| /** |
| * The region <i>selected</i> via fill handle to extend the current |
| * selection for triggering a fill action. Can be <code>null</code>. |
| * |
| * @since 1.4 |
| */ |
| protected Rectangle fillHandleRegion; |
| |
| /** |
| * The bottom right cell in a contiguous selection or <code>null</code> if |
| * there is no selection or the selection is not contiguous. Needed to |
| * identify the cell on which the fill handle should be rendered. |
| * |
| * @since 1.4 |
| */ |
| protected PositionCoordinate bottomRightInSelection; |
| |
| protected SelectRowCommandHandler selectRowCommandHandler; |
| protected SelectCellCommandHandler selectCellCommandHandler; |
| protected SelectColumnCommandHandler selectColumnCommandHandler; |
| |
| public SelectionLayer(IUniqueIndexLayer underlyingLayer) { |
| this(underlyingLayer, null, true); |
| } |
| |
| public SelectionLayer(IUniqueIndexLayer underlyingLayer, boolean useDefaultConfiguration) { |
| this(underlyingLayer, null, useDefaultConfiguration); |
| } |
| |
| public SelectionLayer(IUniqueIndexLayer underlyingLayer, ISelectionModel selectionModel, boolean useDefaultConfiguration) { |
| super(underlyingLayer); |
| this.underlyingLayer = underlyingLayer; |
| |
| setLayerPainter(new SelectionLayerPainter()); |
| |
| setSelectionModel(selectionModel); |
| |
| this.lastSelectedCell = new PositionCoordinate(this, NO_SELECTION, NO_SELECTION); |
| this.selectionAnchor = new PositionCoordinate(this, NO_SELECTION, NO_SELECTION); |
| |
| this.selectRowCommandHandler = new SelectRowCommandHandler(this); |
| this.selectCellCommandHandler = new SelectCellCommandHandler(this); |
| this.selectColumnCommandHandler = new SelectColumnCommandHandler(this); |
| |
| registerCommandHandlers(); |
| |
| if (useDefaultConfiguration) { |
| addConfiguration(new DefaultSelectionLayerConfiguration()); |
| } |
| } |
| |
| public ISelectionModel getSelectionModel() { |
| return this.selectionModel; |
| } |
| |
| public void setSelectionModel(ISelectionModel selectionModel) { |
| if (this.selectionModel != null) { |
| unregisterEventHandler(this.selectionModel); |
| } |
| this.selectionModel = selectionModel != null ? selectionModel : new SelectionModel(this); |
| registerEventHandler(this.selectionModel); |
| } |
| |
| public void addSelection(Rectangle selection) { |
| if (!selection.equals(getLastSelectedRegion())) { |
| setSelectionAnchor(getLastSelectedCell().columnPosition, getLastSelectedCell().rowPosition); |
| setLastSelectedRegion(selection); |
| } |
| |
| this.selectionModel.addSelection(selection); |
| } |
| |
| public void clear() { |
| clear(true); |
| } |
| |
| public void clear(boolean fireSelectionEvent) { |
| this.selectionModel.clearSelection(); |
| |
| boolean validLastSelectedCell = hasSelection(getLastSelectedCell()); |
| setLastSelectedCell(NO_SELECTION, NO_SELECTION); |
| setLastSelectedRegion(new Rectangle(0, 0, 0, 0)); |
| |
| setSelectionAnchor(NO_SELECTION, NO_SELECTION); |
| |
| if (validLastSelectedCell && fireSelectionEvent) { |
| fireCellSelectionEvent( |
| getLastSelectedCell().columnPosition, |
| getLastSelectedCell().rowPosition, |
| false, false, false); |
| } |
| } |
| |
| public void clearSelection(int columnPosition, int rowPosition) { |
| this.selectionModel.clearSelection(columnPosition, rowPosition); |
| |
| if (getSelectionAnchor().columnPosition == columnPosition |
| && getSelectionAnchor().rowPosition == rowPosition) { |
| setSelectionAnchor(NO_SELECTION, NO_SELECTION); |
| } |
| |
| if (this.selectionModel.isEmpty()) { |
| setLastSelectedCell(NO_SELECTION, NO_SELECTION); |
| setLastSelectedRegion(new Rectangle(0, 0, 0, 0)); |
| } |
| |
| if (getLastSelectedCell().columnPosition == columnPosition |
| && getLastSelectedCell().rowPosition == rowPosition) { |
| setLastSelectedCell(NO_SELECTION, NO_SELECTION); |
| } |
| } |
| |
| public void clearSelection(Rectangle selection) { |
| this.selectionModel.clearSelection(selection); |
| |
| // if the selection anchor is within the selection that is removed |
| // it needs to be cleared also |
| Point anchorPoint = new Point( |
| getSelectionAnchor().columnPosition, |
| getSelectionAnchor().rowPosition); |
| if (selection.contains(anchorPoint)) { |
| setSelectionAnchor(NO_SELECTION, NO_SELECTION); |
| } |
| |
| if (this.selectionModel.isEmpty()) { |
| setLastSelectedCell(NO_SELECTION, NO_SELECTION); |
| setLastSelectedRegion(new Rectangle(0, 0, 0, 0)); |
| } |
| |
| // we need to clear the last selected cell in case the selection in that |
| // rectangle is cleared |
| Point lastSelectedPoint = new Point( |
| getLastSelectedCell().columnPosition, |
| getLastSelectedCell().rowPosition); |
| if (selection.contains(lastSelectedPoint)) { |
| setLastSelectedCell(NO_SELECTION, NO_SELECTION); |
| } |
| } |
| |
| public void selectAll() { |
| Rectangle selection = new Rectangle(0, 0, getColumnCount(), getRowCount()); |
| PositionCoordinate lastSelected = getLastSelectedCell(); |
| if (lastSelected.columnPosition == SelectionLayer.NO_SELECTION |
| || lastSelected.columnPosition >= getColumnCount() |
| || lastSelected.rowPosition == SelectionLayer.NO_SELECTION |
| || lastSelected.rowPosition >= getRowCount()) { |
| // search for the first visible column |
| // typically this is 0/0 but when using 0 sized columns for hiding |
| // columns, this leads to missing repainting |
| int column = 0; |
| for (; column < getColumnCount(); column++) { |
| if (getColumnWidthByPosition(column) > 0) { |
| break; |
| } |
| } |
| int row = 0; |
| for (; row < getRowCount(); row++) { |
| if (getRowHeightByPosition(row) > 0) { |
| break; |
| } |
| } |
| setLastSelectedCell(column, row); |
| } |
| addSelection(selection); |
| fireCellSelectionEvent( |
| getLastSelectedCell().columnPosition, |
| getLastSelectedCell().rowPosition, |
| false, |
| false, |
| false); |
| } |
| |
| // Cell features |
| |
| public boolean isCellPositionSelected(int columnPosition, int rowPosition) { |
| return this.selectionModel.isCellPositionSelected(columnPosition, rowPosition); |
| } |
| |
| public void setSelectedCell(int columnPosition, int rowPosition) { |
| selectCell(columnPosition, rowPosition, false, false); |
| } |
| |
| /** |
| * When extending a selected area via modifier keys, we need to move from |
| * the last selected cell. If we are not extending a selection we need to |
| * move from the <i>selection anchor</i>. |
| * |
| * @param withShiftMask |
| * <code>true</code> if the shift mask is active, |
| * <code>false</code> if not. |
| * @param withControlMask |
| * <code>true</code> if the shift mask is active, |
| * <code>false</code> if not. |
| * @return The cell position coordinate related to this SelectionLayer, from |
| * where to move from. |
| */ |
| protected PositionCoordinate getCellPositionToMoveFrom(boolean withShiftMask, boolean withControlMask) { |
| return (!withShiftMask && !withControlMask) ? getSelectionAnchor() : getLastSelectedCellPosition(); |
| } |
| |
| public PositionCoordinate[] getSelectedCellPositions() { |
| int[] selectedColumnPositions = getSelectedColumnPositions(); |
| Set<Range> selectedRowPositions = getSelectedRowPositions(); |
| |
| List<PositionCoordinate> selectedCells = new LinkedList<>(); |
| |
| for (int columnPositionIndex = 0; columnPositionIndex < selectedColumnPositions.length; columnPositionIndex++) { |
| final int columnPosition = selectedColumnPositions[columnPositionIndex]; |
| |
| for (Range rowIndexRange : selectedRowPositions) { |
| for (int rowPositionIndex = rowIndexRange.start; rowPositionIndex < rowIndexRange.end; rowPositionIndex++) { |
| if (this.selectionModel.isCellPositionSelected(columnPosition, rowPositionIndex)) { |
| selectedCells.add(new PositionCoordinate(this, columnPosition, rowPositionIndex)); |
| } |
| } |
| } |
| } |
| Collections.sort(selectedCells, new PositionCoordinateComparator()); |
| return selectedCells.toArray(new PositionCoordinate[0]); |
| } |
| |
| /** |
| * Retrieves the ILayerCells out of the SelectionLayer that are currently |
| * marked as selected in the SelectionModel. Takes spanning into account. |
| * |
| * @return The selected ILayerCells |
| */ |
| public Collection<ILayerCell> getSelectedCells() { |
| Set<ILayerCell> selectedCells = new LinkedHashSet<>(); |
| |
| PositionCoordinate[] selectedCoords = getSelectedCellPositions(); |
| for (PositionCoordinate coord : selectedCoords) { |
| selectedCells.add(getCellByPosition(coord.columnPosition, coord.rowPosition)); |
| } |
| |
| return selectedCells; |
| } |
| |
| /** |
| * Calculates the selected cells - taking into account Shift and Ctrl key |
| * presses. |
| */ |
| public void selectCell(int columnPosition, int rowPosition, boolean withShiftMask, boolean withControlMask) { |
| this.selectCellCommandHandler.selectCell(columnPosition, rowPosition, withShiftMask, withControlMask); |
| } |
| |
| public void selectRegion(int startColumnPosition, int startRowPosition, int regionWidth, int regionHeight) { |
| if (getLastSelectedRegion() == null) { |
| setLastSelectedRegion(new Rectangle( |
| startColumnPosition, |
| startRowPosition, |
| regionWidth, |
| regionHeight)); |
| } else { |
| setLastSelectedRegion( |
| startColumnPosition, |
| startRowPosition, |
| regionWidth, |
| regionHeight); |
| } |
| this.selectionModel.addSelection(new Rectangle( |
| getLastSelectedRegion().x, |
| getLastSelectedRegion().y, |
| getLastSelectedRegion().width, |
| getLastSelectedRegion().height)); |
| } |
| |
| protected void setLastSelectedRegion(Rectangle region) { |
| if (this.selectionModel instanceof IMarkerSelectionModel) { |
| ((IMarkerSelectionModel) this.selectionModel).setLastSelectedRegion(region); |
| } else { |
| // if the given region is null or the current lastSelectedRegion is |
| // null, simply set the reference |
| if (region == null || this.lastSelectedRegion == null) { |
| this.lastSelectedRegion = region; |
| } else { |
| // we are modifying the values of the current lastSelectedRegion |
| // instead of setting a new reference because of reference |
| // issues in other places |
| this.lastSelectedRegion.x = region.x; |
| this.lastSelectedRegion.y = region.y; |
| this.lastSelectedRegion.width = region.width; |
| this.lastSelectedRegion.height = region.height; |
| } |
| } |
| } |
| |
| protected void setLastSelectedRegion( |
| int startColumnPosition, int startRowPosition, |
| int regionWidth, int regionHeight) { |
| if (this.selectionModel instanceof IMarkerSelectionModel) { |
| ((IMarkerSelectionModel) this.selectionModel).setLastSelectedRegion( |
| startColumnPosition, |
| startRowPosition, |
| regionWidth, |
| regionHeight); |
| } else { |
| this.lastSelectedRegion.x = startColumnPosition; |
| this.lastSelectedRegion.y = startRowPosition; |
| this.lastSelectedRegion.width = regionWidth; |
| this.lastSelectedRegion.height = regionHeight; |
| } |
| } |
| |
| // Selection anchor |
| |
| public PositionCoordinate getSelectionAnchor() { |
| if (this.selectionModel instanceof IMarkerSelectionModel) { |
| Point coordinate = ((IMarkerSelectionModel) this.selectionModel).getSelectionAnchor(); |
| return new PositionCoordinate(this, coordinate.x, coordinate.y); |
| } else { |
| return this.selectionAnchor; |
| } |
| } |
| |
| public void moveSelectionAnchor(int startColumnPositionInRegion, int startRowPosition) { |
| setSelectionAnchor(startColumnPositionInRegion, startRowPosition); |
| } |
| |
| void setSelectionAnchor(int columnPosition, int rowPosition) { |
| if (this.selectionModel instanceof IMarkerSelectionModel) { |
| ((IMarkerSelectionModel) this.selectionModel).setSelectionAnchor( |
| new Point(columnPosition, rowPosition)); |
| } else { |
| this.selectionAnchor.columnPosition = columnPosition; |
| this.selectionAnchor.rowPosition = rowPosition; |
| } |
| } |
| |
| // Last selected |
| |
| public PositionCoordinate getLastSelectedCellPosition() { |
| PositionCoordinate coordinate = getLastSelectedCell(); |
| if (hasSelection(coordinate)) { |
| return coordinate; |
| } else { |
| return null; |
| } |
| } |
| |
| PositionCoordinate getLastSelectedCell() { |
| if (this.selectionModel instanceof IMarkerSelectionModel) { |
| Point coordinate = ((IMarkerSelectionModel) this.selectionModel).getLastSelectedCell(); |
| return new PositionCoordinate(this, coordinate.x, coordinate.y); |
| } else { |
| return this.lastSelectedCell; |
| } |
| } |
| |
| static boolean hasSelection(PositionCoordinate coordinate) { |
| return coordinate.columnPosition != NO_SELECTION |
| && coordinate.rowPosition != NO_SELECTION; |
| } |
| |
| /** |
| * Test if all cells in a given region are selected. |
| * |
| * @param region |
| * The region that should be checked. |
| * @return <code>true</code> if all cells in a region are selected, |
| * <code>false</code> if at least one cell in the region is not |
| * selected. |
| * |
| * @since 1.6 |
| */ |
| public boolean allCellsSelectedInRegion(Rectangle region) { |
| if (region.height == Integer.MAX_VALUE) { |
| // if the region is for a full column selection, simplify the check |
| // for better performance and correctness |
| for (int col = region.x; col < (region.x + region.width); col++) { |
| if (!isColumnPositionFullySelected(col)) { |
| return false; |
| } |
| } |
| } else if (region.width == Integer.MAX_VALUE) { |
| // if the region is for a full row selection, simplify the check |
| // for better performance and correctness |
| for (int row = region.y; row < (region.y + region.height); row++) { |
| if (!isRowPositionFullySelected(row)) { |
| return false; |
| } |
| } |
| } else { |
| for (int col = region.x; col < (region.x + region.width); col++) { |
| for (int row = region.y; row < (region.y + region.height); row++) { |
| if (!isCellPositionSelected(col, row)) { |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| public void setLastSelectedCell(int columnPosition, int rowPosition) { |
| if (this.selectionModel instanceof IMarkerSelectionModel) { |
| ((IMarkerSelectionModel) this.selectionModel).setLastSelectedCell( |
| new Point(columnPosition, rowPosition)); |
| } else { |
| this.lastSelectedCell.columnPosition = columnPosition; |
| this.lastSelectedCell.rowPosition = rowPosition; |
| } |
| } |
| |
| public Rectangle getLastSelectedRegion() { |
| if (this.selectionModel instanceof IMarkerSelectionModel) { |
| return ((IMarkerSelectionModel) this.selectionModel).getLastSelectedRegion(); |
| } else { |
| return this.lastSelectedRegion; |
| } |
| } |
| |
| // Column features |
| |
| public boolean hasColumnSelection() { |
| return getLastSelectedCell().columnPosition != NO_SELECTION; |
| } |
| |
| public int[] getSelectedColumnPositions() { |
| return this.selectionModel.getSelectedColumnPositions(); |
| } |
| |
| public boolean isColumnPositionSelected(int columnPosition) { |
| return this.selectionModel.isColumnPositionSelected(columnPosition); |
| } |
| |
| public int[] getFullySelectedColumnPositions() { |
| return this.selectionModel.getFullySelectedColumnPositions(getRowCount()); |
| } |
| |
| public boolean isColumnPositionFullySelected(int columnPosition) { |
| return this.selectionModel.isColumnPositionFullySelected(columnPosition, getRowCount()); |
| } |
| |
| public void selectColumn( |
| int columnPosition, int rowPosition, |
| boolean withShiftMask, boolean withControlMask) { |
| this.selectColumnCommandHandler.selectColumn( |
| columnPosition, rowPosition, |
| withShiftMask, withControlMask); |
| } |
| |
| // Row features |
| |
| public boolean hasRowSelection() { |
| return getLastSelectedCell().rowPosition != NO_SELECTION; |
| } |
| |
| public int getSelectedRowCount() { |
| return this.selectionModel.getSelectedRowCount(); |
| } |
| |
| public Set<Range> getSelectedRowPositions() { |
| return this.selectionModel.getSelectedRowPositions(); |
| } |
| |
| public boolean isRowPositionSelected(int rowPosition) { |
| return this.selectionModel.isRowPositionSelected(rowPosition); |
| } |
| |
| public int[] getFullySelectedRowPositions() { |
| return this.selectionModel.getFullySelectedRowPositions(getColumnCount()); |
| } |
| |
| public boolean isRowPositionFullySelected(int rowPosition) { |
| return this.selectionModel.isRowPositionFullySelected(rowPosition, getColumnCount()); |
| } |
| |
| public void selectRow( |
| int columnPosition, int rowPosition, |
| boolean withShiftMask, boolean withControlMask) { |
| this.selectRowCommandHandler.selectRows( |
| columnPosition, |
| new int[] { rowPosition }, |
| withShiftMask, |
| withControlMask, |
| rowPosition); |
| } |
| |
| // ILayer methods |
| |
| @Override |
| public String getDisplayModeByPosition(int columnPosition, int rowPosition) { |
| String displayMode = super.getDisplayModeByPosition(columnPosition, rowPosition); |
| if (isCellPositionSelected(columnPosition, rowPosition)) { |
| if (DisplayMode.HOVER.equals(displayMode)) { |
| return DisplayMode.SELECT_HOVER; |
| } |
| return DisplayMode.SELECT; |
| } |
| return displayMode; |
| } |
| |
| @Override |
| public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) { |
| LabelStack labelStack = super.getConfigLabelsByPosition(columnPosition, rowPosition); |
| |
| ILayerCell cell = getCellByPosition(columnPosition, rowPosition); |
| if (cell != null) { |
| Rectangle cellRectangle = new Rectangle( |
| cell.getOriginColumnPosition(), |
| cell.getOriginRowPosition(), |
| cell.getColumnSpan(), |
| cell.getRowSpan()); |
| |
| PositionCoordinate anchor = getSelectionAnchor(); |
| if (cellRectangle.contains(anchor.columnPosition, anchor.rowPosition)) { |
| labelStack.addLabelOnTop(SelectionStyleLabels.SELECTION_ANCHOR_STYLE); |
| } |
| |
| if (this.bottomRightInSelection != null |
| && cellRectangle.contains( |
| this.bottomRightInSelection.columnPosition, |
| this.bottomRightInSelection.rowPosition)) { |
| labelStack.addLabel(SelectionStyleLabels.FILL_HANDLE_CELL); |
| } |
| |
| if (this.fillHandleRegion != null |
| && this.fillHandleRegion.contains(cell.getColumnPosition(), cell.getRowPosition())) { |
| labelStack.addLabel(SelectionStyleLabels.FILL_HANDLE_REGION); |
| } |
| } |
| return labelStack; |
| } |
| |
| // Command handling |
| |
| @Override |
| protected void registerCommandHandlers() { |
| registerCommandHandler(this.selectCellCommandHandler); |
| registerCommandHandler(this.selectRowCommandHandler); |
| registerCommandHandler(this.selectColumnCommandHandler); |
| registerCommandHandler(new SelectRegionCommandHandler(this)); |
| |
| registerCommandHandler(new EditSelectionCommandHandler(this)); |
| registerCommandHandler(new InitializeAutoResizeColumnsCommandHandler(this)); |
| registerCommandHandler(new InitializeAutoResizeRowsCommandHandler(this)); |
| registerCommandHandler(new CopyDataCommandHandler(this)); |
| registerCommandHandler(new SearchGridCellsCommandHandler(this)); |
| } |
| |
| @Override |
| public boolean doCommand(ILayerCommand command) { |
| if (command instanceof SelectAllCommand |
| && command.convertToTargetLayer(this)) { |
| selectAll(); |
| return true; |
| } else if (command instanceof ClearAllSelectionsCommand |
| && command.convertToTargetLayer(this)) { |
| clear(); |
| return true; |
| } else if (command instanceof ColumnHideCommand |
| && command.convertToTargetLayer(this)) { |
| return handleColumnHideCommand((ColumnHideCommand) command); |
| } else if (command instanceof MultiColumnHideCommand |
| && command.convertToTargetLayer(this)) { |
| return handleMultiColumnHideCommand((MultiColumnHideCommand) command); |
| } else if (command instanceof RowHideCommand |
| && command.convertToTargetLayer(this)) { |
| return handleRowHideCommand((RowHideCommand) command); |
| } else if (command instanceof MultiRowHideCommand |
| && command.convertToTargetLayer(this)) { |
| return handleMultiRowHideCommand((MultiRowHideCommand) command); |
| } else if (command instanceof RowPositionHideCommand |
| && command.convertToTargetLayer(this)) { |
| return handleRowPositionHideCommand((RowPositionHideCommand) command); |
| } else if (command instanceof ColumnResizeCommand |
| && command.convertToTargetLayer(this)) { |
| return handleColumnResizeCommand((ColumnResizeCommand) command); |
| } else if (command instanceof RowResizeCommand |
| && command.convertToTargetLayer(this)) { |
| return handleRowResizeCommand((RowResizeCommand) command); |
| } |
| return super.doCommand(command); |
| } |
| |
| public void fireCellSelectionEvent(int columnPosition, int rowPosition, |
| boolean forcingEntireCellIntoViewport, boolean withShiftMask, |
| boolean withControlMask) { |
| |
| int column = columnPosition; |
| int row = rowPosition; |
| if (columnPosition >= 0 && rowPosition >= 0) { |
| ILayerCell cell = getCellByPosition(columnPosition, rowPosition); |
| if (cell != null) { |
| column = cell.getOriginColumnPosition(); |
| row = cell.getOriginRowPosition(); |
| } |
| } |
| final CellSelectionEvent selectionEvent = |
| new CellSelectionEvent(this, |
| column, |
| row, |
| withShiftMask, |
| withControlMask); |
| fireLayerEvent(selectionEvent); |
| } |
| |
| // command transformations |
| |
| /** |
| * Will check if there are fully selected column positions. If there is at |
| * least one fully selected column position, the {@link ColumnHideCommand} |
| * will be consumed and a {@link MultiColumnHideCommand} will be created and |
| * executed further down the layer stack, that contains all fully selected |
| * column positions. Otherwise the given command will be executed further. |
| * <br> |
| * |
| * This is necessary because neither the ColumnHideShowLayer nor the action |
| * that caused the execution of the {@link ColumnHideCommand} is aware of |
| * the presence of the {@link SelectionLayer}. Without this transformation, |
| * only the column on which the action was called will be hidden instead of |
| * all selected ones. |
| * |
| * @param command |
| * The {@link ColumnHideCommand} to process |
| * @return <code>true</code> if the command has been handled, |
| * <code>false</code> otherwise |
| */ |
| protected boolean handleColumnHideCommand(ColumnHideCommand command) { |
| if (isColumnPositionFullySelected(command.getColumnPosition())) { |
| return handleMultiColumnHideCommand(new MultiColumnHideCommand(this, getFullySelectedColumnPositions())); |
| } else { |
| return super.doCommand(command); |
| } |
| } |
| |
| /** |
| * Previous to processing the given {@link MultiColumnHideCommand} down the |
| * layer stack, the fully selected column positions selection state will be |
| * cleared. This is necessary so the selection also disappears for the |
| * selected columns. Otherwise after hiding the selection will be showed for |
| * different columns. |
| * |
| * @param command |
| * The {@link MultiColumnHideCommand} to process |
| * @return <code>true</code> if the command has been handled, |
| * <code>false</code> otherwise |
| */ |
| protected boolean handleMultiColumnHideCommand(MultiColumnHideCommand command) { |
| for (int columnPosition : command.getColumnPositionsArray()) { |
| if (isColumnPositionFullySelected(columnPosition)) { |
| Rectangle selection = new Rectangle( |
| columnPosition, |
| 0, |
| 1, |
| Integer.MAX_VALUE); |
| clearSelection(selection); |
| } |
| } |
| return super.doCommand(command); |
| } |
| |
| /** |
| * Will check if there are fully selected row positions. If there is at |
| * least one fully selected row position, the {@link RowHideCommand} will be |
| * consumed and a {@link MultiRowHideCommand} will be created and executed |
| * further down the layer stack, that contains all fully selected row |
| * positions. Otherwise the given command will be executed further.<br> |
| * |
| * This is necessary because neither the RowHideShowLayer nor the action |
| * that caused the execution of the {@link RowHideCommand} is aware of the |
| * presence of the {@link SelectionLayer}. Without this transformation, only |
| * the row on which the action was called will be hidden instead of all |
| * selected ones. |
| * |
| * @param command |
| * The {@link RowHideCommand} to process |
| * @return <code>true</code> if the command has been handled, |
| * <code>false</code> otherwise |
| */ |
| protected boolean handleRowHideCommand(RowHideCommand command) { |
| if (isRowPositionFullySelected(command.getRowPosition())) { |
| return handleMultiRowHideCommand(new MultiRowHideCommand(this, getFullySelectedRowPositions())); |
| } else { |
| return super.doCommand(command); |
| } |
| } |
| |
| /** |
| * Previous to processing the given {@link MultiRowHideCommand} down the |
| * layer stack, the fully selected row positions selection state will be |
| * cleared. This is necessary so the selection also disappears for the |
| * selected rows. Otherwise after hiding the selection will be showed for |
| * different rows. |
| * |
| * @param command |
| * The {@link MultiRowHideCommand} to process |
| * @return <code>true</code> if the command has been handled, |
| * <code>false</code> otherwise |
| */ |
| protected boolean handleMultiRowHideCommand(MultiRowHideCommand command) { |
| for (int rowPosition : command.getRowPositionsArray()) { |
| if (isRowPositionFullySelected(rowPosition)) { |
| Rectangle selection = new Rectangle( |
| 0, |
| rowPosition, |
| Integer.MAX_VALUE, |
| 1); |
| clearSelection(selection); |
| } |
| } |
| return super.doCommand(command); |
| } |
| |
| /** |
| * First inspects the cell at the position contained in the |
| * {@link RowPositionHideCommand} and calculates all rows for that cell, |
| * taking the spanning into account. If the cell itself is selected, than |
| * also all other selected cells in the column position will be handled for |
| * hiding. |
| * |
| * @param command |
| * The {@link RowPositionHideCommand} to process. |
| * @return <code>true</code> if the command has been handled, |
| * <code>false</code> otherwise |
| * |
| * @since 1.6 |
| */ |
| protected boolean handleRowPositionHideCommand(RowPositionHideCommand command) { |
| ILayerCell cell = getCellByPosition(command.getColumnPosition(), command.getRowPosition()); |
| // first consider spanning |
| MutableIntList positions = IntLists.mutable.ofAll( |
| IntStream.range(cell.getOriginRowPosition(), (cell.getOriginRowPosition() + cell.getRowSpan()))); |
| |
| // then consider selection |
| if (isCellPositionSelected(cell.getColumnPosition(), cell.getOriginRowPosition())) { |
| // if the cell for which the command was triggered is selected, then |
| // also take other selected cells into account |
| PositionCoordinate[] selectedCellPositions = getSelectedCellPositions(); |
| for (PositionCoordinate coord : selectedCellPositions) { |
| // only consider selections in the same column |
| if (coord.getColumnPosition() == command.getColumnPosition()) { |
| positions.add(coord.getRowPosition()); |
| } |
| } |
| } |
| |
| return doCommand(new MultiRowHideCommand(this, positions.distinct().toSortedArray())); |
| } |
| |
| /** |
| * Will check if there are fully selected column positions. If there is at |
| * least one fully selected column position, the {@link ColumnResizeCommand} |
| * will be consumed and a {@link MultiColumnResizeCommand} will be created |
| * and executed further down the layer stack, that contains all fully |
| * selected column positions. Otherwise the given command will be executed |
| * further.<br> |
| * |
| * This is necessary because neither the underlying layers are not aware of |
| * the presence of the {@link SelectionLayer}. Without this transformation, |
| * only the column on which the action was called will be resized instead of |
| * all selected ones. |
| * |
| * @param command |
| * The {@link ColumnResizeCommand} to process |
| * @return <code>true</code> if the command has been handled, |
| * <code>false</code> otherwise |
| */ |
| protected boolean handleColumnResizeCommand(ColumnResizeCommand command) { |
| if (isColumnPositionFullySelected(command.getColumnPosition())) { |
| return super.doCommand( |
| new MultiColumnResizeCommand(this, |
| this.selectionModel.getFullySelectedColumnPositions(getRowCount()), |
| command.getNewColumnWidth(), |
| command.downScaleValue())); |
| } else { |
| return super.doCommand(command); |
| } |
| } |
| |
| /** |
| * Will check if there are fully selected row positions. If there is at |
| * least one fully selected row position, the {@link RowResizeCommand} will |
| * be consumed and a {@link MultiRowResizeCommand} will be created and |
| * executed further down the layer stack, that contains all fully selected |
| * row positions. Otherwise the given command will be executed further.<br> |
| * |
| * This is necessary because neither the underlying layers are not aware of |
| * the presence of the {@link SelectionLayer}. Without this transformation, |
| * only the row on which the action was called will be resized instead of |
| * all selected ones. |
| * |
| * @param command |
| * The {@link RowResizeCommand} to process |
| * @return <code>true</code> if the command has been handled, |
| * <code>false</code> otherwise |
| */ |
| protected boolean handleRowResizeCommand(RowResizeCommand command) { |
| if (isRowPositionFullySelected(command.getRowPosition())) { |
| return super.doCommand( |
| new MultiRowResizeCommand(this, |
| this.selectionModel.getFullySelectedRowPositions(getColumnCount()), |
| command.getNewHeight(), |
| command.downScaleValue())); |
| } else { |
| return super.doCommand(command); |
| } |
| } |
| |
| /** |
| * Set the region that is currently <i>selected</i> via fill handle to |
| * extend the current active selection for triggering a fill action. |
| * |
| * @param region |
| * The region <i>selected</i> via fill handle. |
| * |
| * @see FillHandleDragMode |
| * @see FillHandleLayerPainter |
| * |
| * @since 1.4 |
| */ |
| public void setFillHandleRegion(Rectangle region) { |
| this.fillHandleRegion = region; |
| } |
| |
| /** |
| * Returns the region that is currently <i>selected</i> via fill handle to |
| * extend the current active selection. Used to perform actions on drag |
| * & drop of the fill handle. |
| * |
| * @return The region <i>selected</i> via fill handle or <code>null</code>. |
| * |
| * @since 1.4 |
| */ |
| public Rectangle getFillHandleRegion() { |
| return this.fillHandleRegion; |
| } |
| |
| /** |
| * Marks the bottom right cell in a contiguous selection within the |
| * {@link SelectionLayer}. Also removes the markup in case there is no |
| * selection or the selection is not contiguous. |
| * |
| * @since 1.4 |
| */ |
| public void markFillHandleCell() { |
| ILayerCell bottomRight = SelectionUtils.getBottomRightCellInSelection(this); |
| if (bottomRight != null) { |
| this.bottomRightInSelection = new PositionCoordinate( |
| this, |
| bottomRight.getColumnPosition(), |
| bottomRight.getRowPosition()); |
| } else { |
| this.bottomRightInSelection = null; |
| } |
| } |
| |
| /** |
| * @since 1.4 |
| */ |
| @Override |
| public Collection<String> getProvidedLabels() { |
| Collection<String> labels = super.getProvidedLabels(); |
| |
| labels.add(SelectionStyleLabels.SELECTION_ANCHOR_STYLE); |
| labels.add(SelectionStyleLabels.SELECTION_ANCHOR_GRID_LINE_STYLE); |
| labels.add(SelectionStyleLabels.COLUMN_FULLY_SELECTED_STYLE); |
| labels.add(SelectionStyleLabels.ROW_FULLY_SELECTED_STYLE); |
| labels.add(SelectionStyleLabels.FILL_HANDLE_REGION); |
| labels.add(SelectionStyleLabels.FILL_HANDLE_CELL); |
| labels.add(SelectionStyleLabels.COPY_BORDER_STYLE); |
| |
| return labels; |
| } |
| } |