blob: 08799902ab23e9a867e4b8461493174610ed7e9e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019, 2020 Dirk Fauth.
*
* 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:
* Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.group.performance.command;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
import org.eclipse.nebula.widgets.nattable.group.RowGroupUtils;
import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel;
import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
import org.eclipse.nebula.widgets.nattable.group.performance.RowGroupHeaderLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
import org.eclipse.nebula.widgets.nattable.reorder.command.MultiRowReorderCommand;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
/**
* Command handler for the {@link MultiRowReorderCommand} that is registered on
* the positionLayer of the {@link RowGroupHeaderLayer} to avoid handling in
* case the reordering would break an unbreakable group.
*
* @since 1.6
*/
public class GroupMultiRowReorderCommandHandler extends AbstractLayerCommandHandler<MultiRowReorderCommand> {
private final RowGroupHeaderLayer rowGroupHeaderLayer;
public GroupMultiRowReorderCommandHandler(RowGroupHeaderLayer rowGroupHeaderLayer) {
this.rowGroupHeaderLayer = rowGroupHeaderLayer;
}
@Override
protected boolean doCommand(MultiRowReorderCommand command) {
List<Integer> fromRowPositions = command.getFromRowPositions();
int toRowPosition = command.getToRowPosition();
boolean reorderToTopEdge = command.isReorderToTopEdge();
MoveDirectionEnum moveDirection = PositionUtil.getVerticalMoveDirection(fromRowPositions.get(0), toRowPosition);
if (!RowGroupUtils.isBetweenTwoGroups(
this.rowGroupHeaderLayer,
toRowPosition,
reorderToTopEdge,
moveDirection)) {
for (int fromRowPosition : fromRowPositions) {
if (!RowGroupUtils.isReorderValid(this.rowGroupHeaderLayer, fromRowPosition, toRowPosition, reorderToTopEdge)) {
// consume as the reorder is not valid
return true;
}
}
}
// as we are registered on the positionLayer, there is no need
// for transformation
int toPositionToCheck = toRowPosition;
if (MoveDirectionEnum.DOWN == moveDirection && reorderToTopEdge) {
toPositionToCheck--;
}
// check if there are collapsed from groups
Map<GroupModel, Set<Group>> collapsed = new HashMap<>();
for (int level = 0; level < this.rowGroupHeaderLayer.getLevelCount(); level++) {
GroupModel model = this.rowGroupHeaderLayer.getGroupModel(level);
for (int fromRowPosition : fromRowPositions) {
Group fromGroup = this.rowGroupHeaderLayer.getGroupByPosition(level, fromRowPosition);
if (fromGroup != null && fromGroup.isCollapsed()) {
Set<Group> collapsedGroups = collapsed.get(model);
if (collapsedGroups == null) {
collapsedGroups = new HashSet<>();
collapsed.put(model, collapsedGroups);
}
collapsedGroups.add(fromGroup);
}
}
}
// if there are collapsed groups collect from indexes and to index
if (!collapsed.isEmpty()) {
int[] fromIndexes = fromRowPositions.stream()
.mapToInt(this.rowGroupHeaderLayer.getPositionLayer()::getRowIndexByPosition)
.toArray();
int toIndex = this.rowGroupHeaderLayer.getPositionLayer().getRowIndexByPosition(toRowPosition);
// expand all collapsed groups
collapsed.forEach((model, collapsedGroups) -> collapsedGroups.forEach(group -> this.rowGroupHeaderLayer.expandGroup(model, group)));
// update fromColumnPositions and toColumnPosition
int[] fromPositions = Arrays.stream(fromIndexes)
.map(this.rowGroupHeaderLayer.getPositionLayer()::getRowPositionByIndex)
.toArray();
command.updateFromRowPositions(fromPositions);
toPositionToCheck = this.rowGroupHeaderLayer.getPositionLayer().getRowPositionByIndex(toIndex);
command.updateToRowPosition(toPositionToCheck);
if (MoveDirectionEnum.RIGHT == moveDirection && reorderToTopEdge) {
toPositionToCheck--;
}
}
boolean toggleCoordinateByEdge = false;
Group groupToEnd = null;
Group groupToStart = null;
for (int level = 0; level < this.rowGroupHeaderLayer.getLevelCount(); level++) {
Group toGroup = this.rowGroupHeaderLayer.getGroupByPosition(level, toPositionToCheck);
if (toGroup != null && MoveDirectionEnum.DOWN == moveDirection && toGroup.isGroupEnd(toPositionToCheck)) {
toggleCoordinateByEdge = true;
if (toGroup.isUnbreakable() && toGroup.getVisibleSpan() < toGroup.getOriginalSpan()) {
groupToEnd = toGroup;
}
} else if (toGroup != null
&& MoveDirectionEnum.UP == moveDirection
&& toGroup.isGroupStart(toPositionToCheck)
&& toGroup.isUnbreakable()
&& toGroup.getVisibleSpan() < toGroup.getOriginalSpan()) {
groupToStart = toGroup;
}
}
if (toggleCoordinateByEdge) {
command.toggleCoordinateByEdge();
}
if (groupToEnd != null) {
// if the group is unbreakable and the visible span is
// smaller than the original span, it could be that
// positions at the end are hidden and the reorder to
// the right edge could lead to a broken group
return this.rowGroupHeaderLayer.getPositionLayer().getUnderlyingLayerByPosition(0, 0).doCommand(
new MultiRowReorderToGroupEndCommand(command, groupToEnd));
} else if (groupToStart != null) {
// if the group is unbreakable and the visible span is
// smaller than the original span, it could be that
// positions at the start are hidden and the reorder to
// the left edge could lead to a broken group
return this.rowGroupHeaderLayer.getPositionLayer().getUnderlyingLayerByPosition(0, 0).doCommand(
new MultiRowReorderToGroupStartCommand(command, groupToStart));
}
return false;
}
@Override
public Class<MultiRowReorderCommand> getCommandClass() {
return MultiRowReorderCommand.class;
}
/**
* Specialization of the {@link MultiRowReorderCommand} to be able to
* reorder a row to the start of a {@link Group} even the first rows in the
* {@link Group} are hidden.
*
* @since 2.0
*/
class MultiRowReorderToGroupStartCommand extends MultiRowReorderCommand {
private final Group group;
public MultiRowReorderToGroupStartCommand(MultiRowReorderCommand command, Group group) {
super(command);
this.group = group;
}
/**
* Clone constructor.
*
* @param command
* The command to clone.
*/
protected MultiRowReorderToGroupStartCommand(MultiRowReorderToGroupStartCommand command) {
super(command);
this.group = command.group;
}
@Override
public boolean convertToTargetLayer(ILayer targetLayer) {
boolean convert = super.convertToTargetLayer(targetLayer);
// check if there are positions for the group members that would be
// more to the left. this could happen e.g. if rows at the group
// start are hidden
if (convert && isReorderToTopEdge() && targetLayer instanceof IUniqueIndexLayer) {
int groupStartPosition = ((IUniqueIndexLayer) targetLayer).getRowPositionByIndex(this.group.getStartIndex());
if (groupStartPosition >= 0 && groupStartPosition < getToRowPosition()) {
this.toRowPositionCoordinate.rowPosition = groupStartPosition;
}
}
return convert;
}
@Override
public MultiRowReorderToGroupStartCommand cloneCommand() {
return new MultiRowReorderToGroupStartCommand(this);
}
}
/**
* Specialization of the {@link MultiRowReorderCommand} to be able to
* reorder a row to the end of a {@link Group} even the last rows in the
* {@link Group} are hidden.
*
* @since 2.0
*/
class MultiRowReorderToGroupEndCommand extends MultiRowReorderCommand {
private final Group group;
public MultiRowReorderToGroupEndCommand(MultiRowReorderCommand command, Group group) {
super(command);
this.group = group;
}
/**
* Clone constructor.
*
* @param command
* The command to clone.
*/
protected MultiRowReorderToGroupEndCommand(MultiRowReorderToGroupEndCommand command) {
super(command);
this.group = command.group;
}
@Override
public boolean convertToTargetLayer(ILayer targetLayer) {
boolean convert = super.convertToTargetLayer(targetLayer);
// check if there are positions for the group members that would be
// more to the right. this could happen e.g. if rows at the group
// end are hidden
if (convert && !isReorderToTopEdge() && targetLayer instanceof IUniqueIndexLayer) {
int groupEndPosition = this.group.getGroupEndPosition((IUniqueIndexLayer) targetLayer);
if (groupEndPosition >= 0 && groupEndPosition > getToRowPosition()) {
this.toRowPositionCoordinate.rowPosition = groupEndPosition;
}
}
return convert;
}
@Override
public MultiRowReorderToGroupEndCommand cloneCommand() {
return new MultiRowReorderToGroupEndCommand(this);
}
}
}