/*******************************************************************************
* Copyright (c) 2012, 2016 Original authors and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Original authors and others - initial API and implementation
* Jonas Hugo <Jonas.Hugo@jeppesen.com>,
* Markus Wahl <Markus.Wahl@jeppesen.com> - Use getters and setters for
* the markers of SelectionLayer instead of the fields.
* Dirk Fauth <dirk.fauth@googlemail.com> - Bug 447259, 447261
* Vincent Lorenzo <vincent.lorenzo@cea.fr> - Bug 478622
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.selection;
import static org.eclipse.nebula.widgets.nattable.selection.SelectionUtils.bothShiftAndControl;
import static org.eclipse.nebula.widgets.nattable.selection.SelectionUtils.isControlOnly;
import static org.eclipse.nebula.widgets.nattable.selection.SelectionUtils.isShiftOnly;
import static org.eclipse.nebula.widgets.nattable.selection.SelectionUtils.noShiftOrControl;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
import org.eclipse.nebula.widgets.nattable.coordinate.Range;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.selection.command.SelectRowsCommand;
import org.eclipse.nebula.widgets.nattable.selection.event.RowSelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
public class SelectRowCommandHandler implements ILayerCommandHandler<SelectRowsCommand> {
private final SelectionLayer selectionLayer;
public SelectRowCommandHandler(SelectionLayer selectionLayer) {
this.selectionLayer = selectionLayer;
}
@Override
public boolean doCommand(ILayer targetLayer, SelectRowsCommand command) {
if (command.convertToTargetLayer(this.selectionLayer)) {
selectRows(
command.getColumnPosition(),
command.getRowPositions(),
command.isWithShiftMask(),
command.isWithControlMask(),
command.getRowPositionToMoveIntoViewport());
return true;
}
return false;
}
/**
* Performs row selection based on the given informations and fires a
* {@link RowSelectionEvent} for the changed selection.
*
* @param columnPosition
* The column position of the {@link SelectRowsCommand}.
* @param rowPositions
* The row position of the {@link SelectRowsCommand}.
* @param withShiftMask
* The shift mask information of the {@link SelectRowsCommand}.
* @param withControlMask
* The control mask information of the {@link SelectRowsCommand}.
* @param rowPositionToMoveIntoViewport
* Information which row should be moved to the viewport,
* transported by the {@link SelectRowsCommand}.
*/
protected void selectRows(
int columnPosition, Collection<Integer> rowPositions,
boolean withShiftMask, boolean withControlMask,
int rowPositionToMoveIntoViewport) {
Set<Range> changedRowRanges = new HashSet<Range>();
for (int rowPosition : rowPositions) {
changedRowRanges.addAll(
internalSelectRow(
columnPosition, rowPosition,
withShiftMask, withControlMask));
}
Set<Integer> changedRows = new HashSet<Integer>();
for (Range range : changedRowRanges) {
for (int i = range.start; i < range.end; i++) {
changedRows.add(Integer.valueOf(i));
}
}
this.selectionLayer.fireLayerEvent(
new RowSelectionEvent(
this.selectionLayer, changedRows, rowPositionToMoveIntoViewport, withShiftMask, withControlMask));
}
/**
* Delegates the selection operations to execute regarding the state
* modifier keys.
*
* @param columnPosition
* The column position of the {@link SelectRowsCommand}.
* @param rowPositions
* The row position of the {@link SelectRowsCommand}.
* @param withShiftMask
* The shift mask information of the {@link SelectRowsCommand}.
* @param withControlMask
* The control mask information of the {@link SelectRowsCommand}.
* @return The changed selection.
*/
private Set<Range> internalSelectRow(
int columnPosition, int rowPosition,
boolean withShiftMask, boolean withControlMask) {
Set<Range> changedRowRanges = new HashSet<Range>();
if (noShiftOrControl(withShiftMask, withControlMask)) {
changedRowRanges.addAll(this.selectionLayer.getSelectedRowPositions());
this.selectionLayer.clear(false);
this.selectionLayer.selectCell(0, rowPosition, withShiftMask, withControlMask);
this.selectionLayer.selectRegion(0, rowPosition, Integer.MAX_VALUE, 1);
this.selectionLayer.moveSelectionAnchor(columnPosition, rowPosition);
changedRowRanges.add(new Range(rowPosition, rowPosition + 1));
} else if (bothShiftAndControl(withShiftMask, withControlMask)) {
changedRowRanges.add(selectRowWithShiftKey(columnPosition, rowPosition));
} else if (isShiftOnly(withShiftMask, withControlMask)) {
changedRowRanges.add(selectRowWithShiftKey(columnPosition, rowPosition));
} else if (isControlOnly(withShiftMask, withControlMask)) {
changedRowRanges.add(selectRowWithCtrlKey(columnPosition, rowPosition));
}
this.selectionLayer.setLastSelectedCell(columnPosition, rowPosition);
return changedRowRanges;
}
/**
* Performs selection operations with pressed CTRL modifier.
*
* @param columnPosition
* The column position of the {@link SelectRowsCommand}. Needed
* to move the selection anchor.
* @param rowPositions
* The row position of the {@link SelectRowsCommand}.
* @return The changed selection.
*/
private Range selectRowWithCtrlKey(int columnPosition, int rowPosition) {
Rectangle selectedRowRectangle =
new Rectangle(0, rowPosition, Integer.MAX_VALUE, 1);
if (this.selectionLayer.isRowPositionFullySelected(rowPosition)) {
this.selectionLayer.clearSelection(selectedRowRectangle);
this.selectionLayer.setLastSelectedRegion(null);
// if there is still a row selected but no selection anchor, we
// need to set one for a consistent state
int[] selectedRows = this.selectionLayer.getFullySelectedRowPositions();
if (selectedRows.length > 0
&& this.selectionLayer.getSelectionAnchor().rowPosition == SelectionLayer.NO_SELECTION) {
// determine row to move the anchor to
int toPos = selectedRows[0];
for (int i = 0; i < selectedRows.length; i++) {
if (selectedRows[i] < rowPosition) {
toPos = selectedRows[i];
} else {
break;
}
}
this.selectionLayer.moveSelectionAnchor(columnPosition, toPos);
}
} else {
// Preserve last selected region
if (this.selectionLayer.getLastSelectedRegion() != null) {
this.selectionLayer.selectRegion(
this.selectionLayer.getLastSelectedRegion().x,
this.selectionLayer.getLastSelectedRegion().y,
this.selectionLayer.getLastSelectedRegion().width,
this.selectionLayer.getLastSelectedRegion().height);
}
this.selectionLayer.selectRegion(0, rowPosition, Integer.MAX_VALUE, 1);
this.selectionLayer.moveSelectionAnchor(columnPosition, rowPosition);
}
return new Range(rowPosition, rowPosition + 1);
}
/**
* Performs selection operations with pressed SHIFT modifier.
*
* @param columnPosition
* The column position of the {@link SelectRowsCommand}. Needed
* to move the selection anchor.
* @param rowPositions
* The row position of the {@link SelectRowsCommand}.
* @return The changed selection.
*/
private Range selectRowWithShiftKey(int columnPosition, int rowPosition) {
int numOfRowsToIncludeInRegion = 1;
int startRowPosition = rowPosition;
// as this method will return the whole range based on the selection
// anchor and the clicked position, we clear the selection prior adding
// the newly calculated selection.
Rectangle lastSelectedRegion = this.selectionLayer.getLastSelectedRegion();
if (lastSelectedRegion != null) {
this.selectionLayer.getSelectionModel().clearSelection(lastSelectedRegion);
} else {
this.selectionLayer.getSelectionModel().clearSelection();
}
// if multiple selection is disabled, we need to ensure to only moving
// the selection anchor
if (!this.selectionLayer.getSelectionModel().isMultipleSelectionAllowed()) {
this.selectionLayer.moveSelectionAnchor(columnPosition, rowPosition);
}
if (this.selectionLayer.getLastSelectedRegion() != null) {
numOfRowsToIncludeInRegion =
Math.abs(this.selectionLayer.getSelectionAnchor().rowPosition - rowPosition) + 1;
if (startRowPosition < this.selectionLayer.getSelectionAnchor().rowPosition) {
// Selecting above
startRowPosition = rowPosition;
} else {
// Selecting below
startRowPosition = this.selectionLayer.getSelectionAnchor().rowPosition;
}
}
this.selectionLayer.selectRegion(
0, startRowPosition, Integer.MAX_VALUE, numOfRowsToIncludeInRegion);
return new Range(startRowPosition, startRowPosition + numOfRowsToIncludeInRegion);
}
@Override
public Class<SelectRowsCommand> getCommandClass() {
return SelectRowsCommand.class;
}
}