| /******************************************************************************* |
| * 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); |
| } |
| } |
| |
| } |