blob: 2e88b3f39e1d2354d1c341dfd1f13b62205be7b4 [file] [log] [blame]
/*******************************************************************************
* 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
* &amp; 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;
}
}