blob: 6b0265d49b0e3e37fbdddaa20def0f18e407cefa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Dirk Fauth.
* 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:
* Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.group.performance.action;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.command.LayerCommandUtil;
import org.eclipse.nebula.widgets.nattable.coordinate.ColumnPositionCoordinate;
import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
import org.eclipse.nebula.widgets.nattable.coordinate.RowPositionCoordinate;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupUtils;
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.group.performance.command.ColumnGroupReorderEndCommand;
import org.eclipse.nebula.widgets.nattable.group.performance.command.ColumnGroupReorderStartCommand;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.reorder.action.ColumnReorderDragMode;
import org.eclipse.nebula.widgets.nattable.reorder.command.ColumnReorderEndCommand;
import org.eclipse.nebula.widgets.nattable.reorder.command.ColumnReorderStartCommand;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.nebula.widgets.nattable.selection.command.ClearAllSelectionsCommand;
import org.eclipse.nebula.widgets.nattable.ui.action.IDragMode;
import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeDetectUtil;
import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
/**
* Default {@link IDragMode} invoked for 'left click + drag' on the column group
* header.
* <p>
* It overrides the isValidTargetColumnPosition() to calculate if a destination
* position is valid for the column group to be reordered to.
* <p>
* Example, a column group cannot be reordered to be inside another column
* group.
*
* @since 1.6
*/
public class ColumnGroupHeaderReorderDragMode extends ColumnReorderDragMode {
protected final ColumnGroupHeaderLayer columnGroupHeaderLayer;
protected int level;
protected int dragFromGridRowPosition;
/**
*
* @param columnGroupHeaderLayer
* The {@link ColumnGroupHeaderLayer} to which this drag mode
* should be assigned to.
*/
public ColumnGroupHeaderReorderDragMode(ColumnGroupHeaderLayer columnGroupHeaderLayer) {
this.columnGroupHeaderLayer = columnGroupHeaderLayer;
}
@Override
public void mouseDown(NatTable natTable, MouseEvent event) {
this.natTable = natTable;
this.initialEvent = event;
this.currentEvent = this.initialEvent;
this.dragFromGridColumnPosition = this.natTable.getColumnPositionByX(this.initialEvent.x);
this.dragFromGridRowPosition = this.natTable.getRowPositionByY(this.initialEvent.y);
RowPositionCoordinate convertedRow =
LayerCommandUtil.convertRowPositionToTargetContext(
new RowPositionCoordinate(natTable, this.dragFromGridRowPosition),
this.columnGroupHeaderLayer);
ColumnPositionCoordinate convertedColumn =
LayerCommandUtil.convertColumnPositionToTargetContext(
new ColumnPositionCoordinate(natTable, this.dragFromGridColumnPosition),
this.columnGroupHeaderLayer);
calculateLevel(convertedRow.getRowPosition(), convertedColumn.getColumnPosition());
natTable.addOverlayPainter(this.targetOverlayPainter);
natTable.doCommand(new ClearAllSelectionsCommand());
fireMoveStartCommand(natTable, this.dragFromGridColumnPosition);
}
@Override
protected boolean isValidTargetColumnPosition(ILayer natLayer, int fromGridColumnPosition, int toGridColumnPosition) {
// check if the reordered level supports reordering
if (!this.columnGroupHeaderLayer.isReorderSupportedOnLevel(this.level)) {
return false;
}
if (this.currentEvent != null) {
// if this method was triggered by a mouse event, we determine the
// to column position by the event
// if there is no current mouse event referenced it means the
// reorder is triggered programmatically
CellEdgeEnum moveDirection = getMoveDirection(this.currentEvent.x);
toGridColumnPosition = getDragToGridColumnPosition(
moveDirection,
this.natTable.getColumnPositionByX(this.currentEvent.x));
}
// the drag mode is triggered on the top most layer, e.g. the NatTable
// itself, therefore the grid position needs to be converted to the
// column group header, but as this one is not an IUniqueIndexLayer, we
// need to convert directly to the corresponding position layer
int toPosition = LayerUtil.convertColumnPosition(natLayer, toGridColumnPosition, this.columnGroupHeaderLayer.getPositionLayer());
// Allow moving within the unbreakable group
int fromPosition = this.columnGroupHeaderLayer.getReorderFromColumnPosition();
for (int lvl = (this.level + 1); lvl < this.columnGroupHeaderLayer.getLevelCount(); lvl++) {
GroupModel model = this.columnGroupHeaderLayer.getGroupModel(lvl);
if (model.isPartOfAnUnbreakableGroup(fromPosition)) {
int toCheck = toPosition;
if (toPosition < 0 && toGridColumnPosition == natLayer.getColumnCount()) {
toCheck = LayerUtil.convertColumnPosition(natLayer, toGridColumnPosition - 1, this.columnGroupHeaderLayer.getPositionLayer());
} else {
MoveDirectionEnum moveDirection = PositionUtil.getHorizontalMoveDirection(fromPosition, toCheck);
toCheck = MoveDirectionEnum.RIGHT == moveDirection ? toCheck - 1 : toCheck;
}
return ColumnGroupUtils.isInTheSameGroup(
this.columnGroupHeaderLayer,
lvl, fromPosition,
toCheck);
}
}
// ensure that the target position is valid on every level above
for (int lvl = (this.level + 1); lvl < this.columnGroupHeaderLayer.getLevelCount(); lvl++) {
GroupModel model = this.columnGroupHeaderLayer.getGroupModel(lvl);
boolean betweenTwoGroups = false;
if (this.currentEvent != null) {
int minX = this.currentEvent.x - GUIHelper.DEFAULT_RESIZE_HANDLE_SIZE;
int maxX = this.currentEvent.x + GUIHelper.DEFAULT_RESIZE_HANDLE_SIZE;
betweenTwoGroups = ColumnGroupUtils.isBetweenTwoGroups(natLayer, minX, maxX, this.columnGroupHeaderLayer, lvl);
}
if (!betweenTwoGroups) {
if (model.isPartOfAnUnbreakableGroup(toPosition)) {
return false;
}
}
}
return ColumnGroupUtils.isBetweenTwoGroups(
this.columnGroupHeaderLayer,
this.level,
toPosition,
toPosition < this.columnGroupHeaderLayer.getColumnCount(),
PositionUtil.getHorizontalMoveDirection(fromGridColumnPosition, toGridColumnPosition));
}
@Override
protected void fireMoveStartCommand(NatTable natTable, int dragFromGridColumnPosition) {
if (this.level >= 0) {
natTable.doCommand(new ColumnGroupReorderStartCommand(natTable, this.level, dragFromGridColumnPosition));
} else {
natTable.doCommand(new ColumnReorderStartCommand(natTable, dragFromGridColumnPosition));
}
}
@Override
protected void fireMoveEndCommand(NatTable natTable, int dragToGridColumnPosition) {
if (this.level >= 0) {
natTable.doCommand(new ColumnGroupReorderEndCommand(natTable, this.level, dragToGridColumnPosition));
} else {
natTable.doCommand(new ColumnReorderEndCommand(natTable, dragToGridColumnPosition));
}
}
@Override
protected CellEdgeEnum getMoveDirection(int x) {
ILayerCell cell = getColumnCell(x);
if (cell != null) {
Rectangle selectedColumnHeaderRect = cell.getBounds();
return CellEdgeDetectUtil.getHorizontalCellEdge(
selectedColumnHeaderRect,
new Point(x, this.natTable.getStartYOfRowPosition(this.dragFromGridRowPosition)));
}
return null;
}
@Override
protected ILayerCell getColumnCell(int x) {
int gridColumnPosition = this.natTable.getColumnPositionByX(x);
return this.natTable.getCellByPosition(gridColumnPosition, this.dragFromGridRowPosition);
}
/**
* Calculate the group level and based on that the real drag from grid row
* position that is reordered. Needed in case there is no group at the
* coordinate level and therefore a spanning indicates a group at a lower
* level.
*
* @param rowPosition
* The row position from which the drag was started. Needs to be
* related to the columnGroupHeaderLayer.
* @param columnPosition
* The column position from which the drag was started. Needed to
* check if there is a group at the calculated level. Needs to be
* related to the columnGroupHeaderLayer.
*/
protected void calculateLevel(int rowPosition, int columnPosition) {
this.level = this.columnGroupHeaderLayer.getLevelForRowPosition(rowPosition);
Group group = this.columnGroupHeaderLayer.getGroupByPosition(this.level, columnPosition);
while (group == null && this.level > 0) {
// decrease the level and increase the from row position as we need
// to check one row below
this.level--;
this.dragFromGridRowPosition++;
group = this.columnGroupHeaderLayer.getGroupByPosition(this.level, columnPosition);
}
if (group == null) {
// no group found, so we set the level to -1 and trigger column
// reordering instead of column group reordering in further steps
this.level = -1;
}
}
}