| /******************************************************************************* |
| * 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.HashMap; |
| import java.util.HashSet; |
| |
| import org.eclipse.collections.api.iterator.MutableIntIterator; |
| import org.eclipse.collections.api.list.primitive.MutableIntList; |
| import org.eclipse.collections.impl.factory.primitive.IntLists; |
| import org.eclipse.nebula.widgets.nattable.Messages; |
| import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.group.command.CreateColumnGroupCommand; |
| import org.eclipse.nebula.widgets.nattable.group.command.DisplayColumnGroupRenameDialogCommand; |
| import org.eclipse.nebula.widgets.nattable.group.command.IColumnGroupCommand; |
| import org.eclipse.nebula.widgets.nattable.group.command.RemoveColumnGroupCommand; |
| import org.eclipse.nebula.widgets.nattable.group.command.UngroupColumnCommand; |
| import org.eclipse.nebula.widgets.nattable.group.event.GroupColumnsEvent; |
| import org.eclipse.nebula.widgets.nattable.group.event.UngroupColumnsEvent; |
| import org.eclipse.nebula.widgets.nattable.group.performance.ColumnGroupHeaderLayer; |
| 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.layer.LayerUtil; |
| import org.eclipse.nebula.widgets.nattable.layer.event.VisualRefreshEvent; |
| import org.eclipse.nebula.widgets.nattable.reorder.command.MultiColumnReorderCommand; |
| import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; |
| import org.eclipse.nebula.widgets.nattable.ui.rename.HeaderRenameDialog; |
| import org.eclipse.nebula.widgets.nattable.ui.rename.HeaderRenameDialog.RenameDialogLabels; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.MessageBox; |
| |
| /** |
| * Command handler for handling {@link IColumnGroupCommand}s to create, remove |
| * and rename column groups. |
| * |
| * @since 1.6 |
| */ |
| public class ColumnGroupsCommandHandler extends AbstractLayerCommandHandler<IColumnGroupCommand> { |
| |
| private final ColumnGroupHeaderLayer contextLayer; |
| private final SelectionLayer selectionLayer; |
| |
| public ColumnGroupsCommandHandler(ColumnGroupHeaderLayer contextLayer, SelectionLayer selectionLayer) { |
| this.contextLayer = contextLayer; |
| this.selectionLayer = selectionLayer; |
| } |
| |
| @Override |
| public boolean doCommand(IColumnGroupCommand command) { |
| if (command instanceof CreateColumnGroupCommand) { |
| CreateColumnGroupCommand createCommand = ((CreateColumnGroupCommand) command); |
| if (!handleCreateColumnGroupCommand(createCommand.getColumnGroupName())) { |
| MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), SWT.INHERIT_DEFAULT | SWT.ICON_ERROR | SWT.OK); |
| messageBox.setText(Messages.getString("ErrorDialog.title")); //$NON-NLS-1$ |
| messageBox.setMessage(Messages.getString("ColumnGroups.selectNonGroupedColumns")); //$NON-NLS-1$ |
| messageBox.open(); |
| } |
| return true; |
| } else if (command instanceof RemoveColumnGroupCommand) { |
| RemoveColumnGroupCommand removeColumnGroupCommand = (RemoveColumnGroupCommand) command; |
| int columnIndex = removeColumnGroupCommand.getColumnIndex(); |
| handleRemoveColumnGroupCommand(columnIndex); |
| return true; |
| } else if (command instanceof UngroupColumnCommand) { |
| handleUngroupCommand(); |
| return true; |
| } else if (command instanceof DisplayColumnGroupRenameDialogCommand) { |
| return displayColumnGroupRenameDialog((DisplayColumnGroupRenameDialogCommand) command); |
| } |
| return false; |
| } |
| |
| /** |
| * Creates a new column group with the given name out of the currently fully |
| * selected column positions. If a selected column is part of an existing |
| * group, the existing group will be removed and all columns belonging to |
| * that group will be also part of the new group. |
| * |
| * @param columnGroupName |
| * The name of the new column group. |
| * @return <code>true</code> if the column group could be created, |
| * <code>false</code> if there are no columns fully selected. |
| */ |
| protected boolean handleCreateColumnGroupCommand(String columnGroupName) { |
| int[] fullySelectedColumns = this.selectionLayer.getFullySelectedColumnPositions(); |
| |
| // we operate on the GroupModel directly to avoid the position |
| // transformation |
| GroupModel model = this.contextLayer.getGroupModel(); |
| |
| MutableIntList positionsToGroup = IntLists.mutable.empty(); |
| if (fullySelectedColumns != null && fullySelectedColumns.length > 0) { |
| for (int column : fullySelectedColumns) { |
| // convert to position layer |
| // needed because the group model takes the positions based on |
| // the position layer |
| int converted = LayerUtil.convertColumnPosition(this.selectionLayer, column, this.contextLayer.getPositionLayer()); |
| if (converted > -1) { |
| positionsToGroup.add(converted); |
| } |
| } |
| |
| HashSet<Group> existingGroups = new HashSet<Group>(); |
| for (MutableIntIterator it = positionsToGroup.intIterator(); it.hasNext();) { |
| int column = it.next(); |
| Group group = model.getGroupByPosition(column); |
| if (group != null) { |
| if (!group.isUnbreakable()) { |
| existingGroups.add(group); |
| } else { |
| // if a position of an unbreakable group was found, we |
| // ignore that position |
| it.remove(); |
| } |
| } |
| } |
| |
| if (!existingGroups.isEmpty()) { |
| // expand those groups |
| this.contextLayer.doCommand(new ColumnGroupExpandCommand(model, existingGroups)); |
| // get all positions from the other groups |
| for (Group group : existingGroups) { |
| positionsToGroup.addAll(group.getVisiblePositions()); |
| // remove existing group and create a new one |
| this.contextLayer.removeGroup(group); |
| } |
| } |
| |
| // bring it to the correct order and remove duplicates |
| positionsToGroup.sortThis(); |
| MutableIntList selectedPositions = IntLists.mutable.ofAll(positionsToGroup.distinct()); |
| |
| if (selectedPositions.size() > 1) { |
| // if a group is created for more than one column, reorder so |
| // the positions are consecutive which is necessary for grouping |
| this.selectionLayer.doCommand( |
| new MultiColumnReorderCommand(this.selectionLayer, selectedPositions.toArray(), selectedPositions.get(0))); |
| } |
| |
| // create the column group |
| this.contextLayer.addGroup(columnGroupName, this.selectionLayer.getColumnIndexByPosition(selectedPositions.get(0)), selectedPositions.size()); |
| |
| this.selectionLayer.clear(); |
| |
| this.contextLayer.fireLayerEvent(new GroupColumnsEvent(this.contextLayer)); |
| |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Remove the column group at the given column index. |
| * |
| * @param columnIndex |
| * The column index to retrieve the column group to remove. |
| */ |
| protected void handleRemoveColumnGroupCommand(int columnIndex) { |
| int selectedPosition = this.selectionLayer.getColumnPositionByIndex(columnIndex); |
| int converted = LayerUtil.convertColumnPosition(this.selectionLayer, selectedPosition, this.contextLayer.getPositionLayer()); |
| GroupModel model = this.contextLayer.getGroupModel(); |
| Group group = model.getGroupByPosition(converted); |
| if (group != null && !group.isUnbreakable()) { |
| if (group.isCollapsed()) { |
| this.contextLayer.doCommand(new ColumnGroupExpandCommand(model, group)); |
| } |
| model.removeGroup(group); |
| |
| this.contextLayer.fireLayerEvent(new GroupColumnsEvent(this.contextLayer)); |
| } |
| } |
| |
| /** |
| * Remove the currently fully selected columns from their corresponding |
| * groups. Will also trigger a reorder to ensure a consistent group |
| * rendering |
| */ |
| protected void handleUngroupCommand() { |
| // Grab fully selected column positions |
| int[] fullySelectedColumns = this.selectionLayer.getFullySelectedColumnPositions(); |
| |
| if (fullySelectedColumns != null && fullySelectedColumns.length > 0) { |
| MutableIntList positionsToUngroup = IntLists.mutable.empty(); |
| for (int column : fullySelectedColumns) { |
| // convert to position layer |
| // needed because the group model takes the positions based on |
| // the position layer |
| int converted = LayerUtil.convertColumnPosition(this.selectionLayer, column, this.contextLayer.getPositionLayer()); |
| if (converted > -1) { |
| positionsToUngroup.add(converted); |
| } |
| } |
| |
| // we operate on the GroupModel directly to avoid the position |
| // transformation |
| GroupModel model = this.contextLayer.getGroupModel(); |
| HashMap<Group, MutableIntList> toRemove = new HashMap<>(); |
| positionsToUngroup.forEach(pos -> { |
| Group group = model.getGroupByPosition(pos); |
| if (group != null) { |
| int endPos = group.getVisibleStartPosition() + group.getVisibleSpan(); |
| if (pos < endPos && !group.isGroupStart(pos)) { |
| // remember position to remove |
| MutableIntList remove = toRemove.get(group); |
| if (remove == null) { |
| remove = IntLists.mutable.empty(); |
| toRemove.put(group, remove); |
| } |
| remove.add(pos); |
| } else { |
| model.removePositionsFromGroup(group, pos); |
| } |
| } |
| }); |
| |
| if (!toRemove.isEmpty()) { |
| toRemove.entrySet().forEach(entry -> { |
| Group group = entry.getKey(); |
| int endPos = group.getVisibleStartPosition() + group.getVisibleSpan(); |
| |
| this.selectionLayer.doCommand(new MultiColumnReorderCommand(this.selectionLayer, entry.getValue().toArray(), endPos)); |
| |
| MutableIntList value = entry.getValue(); |
| int start = endPos - value.size(); |
| int[] positionsToRemove = new int[value.size()]; |
| for (int i = 0; i < entry.getValue().size(); i++) { |
| positionsToRemove[i] = start + i; |
| } |
| |
| model.removePositionsFromGroup(group, positionsToRemove); |
| }); |
| } |
| |
| this.selectionLayer.clear(); |
| |
| this.contextLayer.fireLayerEvent(new UngroupColumnsEvent(this.contextLayer)); |
| } |
| } |
| |
| // TODO NatTable 2.0 - Dialog should not be opened by the command handler |
| protected boolean displayColumnGroupRenameDialog(DisplayColumnGroupRenameDialogCommand command) { |
| int columnPosition = command.getColumnPosition(); |
| |
| HeaderRenameDialog dialog = new HeaderRenameDialog(Display.getDefault().getActiveShell(), null, null, RenameDialogLabels.COLUMN_RENAME); |
| Rectangle colHeaderBounds = this.contextLayer.getBoundsByPosition(columnPosition, 0); |
| Point point = new Point(colHeaderBounds.x, colHeaderBounds.y + colHeaderBounds.height); |
| dialog.setLocation(command.toDisplayCoordinates(point)); |
| dialog.open(); |
| |
| if (!dialog.isCancelPressed()) { |
| Group columnGroup = this.contextLayer.getGroupByPosition(columnPosition); |
| columnGroup.setName(dialog.getNewLabel()); |
| this.contextLayer.fireLayerEvent(new VisualRefreshEvent(this.contextLayer)); |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public Class<IColumnGroupCommand> getCommandClass() { |
| return IColumnGroupCommand.class; |
| } |
| |
| } |