/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.uberfire.ext.wires.core.grids.client.widget.grid.selections.impl;
import java.util.List;
import com.ait.lienzo.client.core.shape.Group;
import com.ait.lienzo.client.core.types.Point2D;
import org.uberfire.client.callbacks.Callback;
import org.uberfire.commons.validation.PortablePreconditions;
import org.uberfire.ext.wires.core.grids.client.model.GridCell;
import org.uberfire.ext.wires.core.grids.client.model.GridCellValue;
import org.uberfire.ext.wires.core.grids.client.model.GridColumn;
import org.uberfire.ext.wires.core.grids.client.model.GridData;
import org.uberfire.ext.wires.core.grids.client.util.ColumnIndexUtilities;
import org.uberfire.ext.wires.core.grids.client.util.CoordinateUtilities;
import org.uberfire.ext.wires.core.grids.client.widget.context.GridBodyCellRenderContext;
import org.uberfire.ext.wires.core.grids.client.widget.grid.GridWidget;
import org.uberfire.ext.wires.core.grids.client.widget.grid.renderers.grids.GridRenderer;
import org.uberfire.ext.wires.core.grids.client.widget.grid.renderers.grids.impl.BaseGridRendererHelper;
import org.uberfire.ext.wires.core.grids.client.widget.grid.selections.CellSelectionManager;
import org.uberfire.ext.wires.core.grids.client.widget.grid.selections.CellSelectionStrategy;
import org.uberfire.ext.wires.core.grids.client.widget.grid.selections.SelectionExtension;
/**
* Manager for Cell selection operations.
*/
public class BaseCellSelectionManager implements CellSelectionManager {
private final GridWidget gridWidget;
private final GridData gridModel;
public BaseCellSelectionManager(final GridWidget gridWidget) {
this.gridWidget = PortablePreconditions.checkNotNull("gridWidget",
gridWidget);
this.gridModel = PortablePreconditions.checkNotNull("gridModel",
gridWidget.getModel());
}
@Override
public boolean selectCell(final Point2D ap,
final boolean isShiftKeyDown,
final boolean isControlKeyDown) {
final Integer uiRowIndex = CoordinateUtilities.getUiRowIndex(gridWidget,
ap.getY());
final Integer uiColumnIndex = CoordinateUtilities.getUiColumnIndex(gridWidget,
ap.getX());
if (uiRowIndex == null || uiColumnIndex == null) {
return false;
}
return selectCell(uiRowIndex,
uiColumnIndex,
isShiftKeyDown,
isControlKeyDown);
}
@Override
public boolean selectCell(final int uiRowIndex,
final int uiColumnIndex,
final boolean isShiftKeyDown,
final boolean isControlKeyDown) {
if (uiRowIndex < 0 || uiRowIndex > gridModel.getRowCount() - 1) {
return false;
}
if (uiColumnIndex < 0 || uiColumnIndex > gridModel.getColumnCount() - 1) {
return false;
}
CellSelectionStrategy strategy;
final GridCell<?> cell = gridModel.getCell(uiRowIndex,
uiColumnIndex);
if (cell == null) {
strategy = RangeSelectionStrategy.INSTANCE;
} else {
strategy = cell.getSelectionManager();
}
if (strategy == null) {
return false;
}
//Handle selection
return strategy.handleSelection(gridModel,
uiRowIndex,
uiColumnIndex,
isShiftKeyDown,
isControlKeyDown);
}
@Override
public boolean adjustSelection(final SelectionExtension direction,
final boolean isShiftKeyDown) {
final GridData.SelectedCell origin = gridModel.getSelectedCellsOrigin();
if (origin == null) {
return false;
}
if (isShiftKeyDown) {
return extendSelection(origin,
direction);
} else {
return moveSelection(origin,
direction);
}
}
private boolean extendSelection(final GridData.SelectedCell origin,
final SelectionExtension direction) {
if (gridModel.getSelectedCells().isEmpty()) {
return false;
}
final int originUiRowIndex = origin.getRowIndex();
final int originUiColumnIndex = ColumnIndexUtilities.findUiColumnIndex(gridModel.getColumns(),
origin.getColumnIndex());
final int minUiRowIndex = findMinUiRowIndex(origin);
final int maxUiRowIndex = findMaxUiRowIndex(origin);
final int minUiColumnIndex = findMinUiColumnIndex(origin);
final int maxUiColumnIndex = findMaxUiColumnIndex(origin);
final int proposedUiColumnIndex = direction.getNextX(minUiColumnIndex,
maxUiColumnIndex,
originUiColumnIndex);
final int proposedUiRowIndex = direction.getNextY(minUiRowIndex,
maxUiRowIndex,
originUiRowIndex);
if (!isCoordinateWithinExtents(proposedUiRowIndex,
proposedUiColumnIndex)) {
return false;
}
return selectCell(proposedUiRowIndex,
proposedUiColumnIndex,
true,
false);
}
private int findMinUiRowIndex(final GridData.SelectedCell origin) {
int minUiRowIndex = origin.getRowIndex();
final List<GridData.SelectedCell> selectedCells = gridModel.getSelectedCells();
for (GridData.SelectedCell selectedCell : selectedCells) {
minUiRowIndex = Math.min(selectedCell.getRowIndex(),
minUiRowIndex);
}
return minUiRowIndex;
}
private int findMaxUiRowIndex(final GridData.SelectedCell origin) {
int maxUiRowIndex = origin.getRowIndex();
final List<GridData.SelectedCell> selectedCells = gridModel.getSelectedCells();
for (GridData.SelectedCell selectedCell : selectedCells) {
maxUiRowIndex = Math.max(selectedCell.getRowIndex(),
maxUiRowIndex);
}
return maxUiRowIndex;
}
private int findMinUiColumnIndex(final GridData.SelectedCell origin) {
int minUiColumnIndex = ColumnIndexUtilities.findUiColumnIndex(gridModel.getColumns(),
origin.getColumnIndex());
final List<GridData.SelectedCell> selectedCells = gridModel.getSelectedCells();
for (GridData.SelectedCell selectedCell : selectedCells) {
minUiColumnIndex = Math.min(ColumnIndexUtilities.findUiColumnIndex(gridModel.getColumns(),
selectedCell.getColumnIndex()),
minUiColumnIndex);
}
return minUiColumnIndex;
}
private int findMaxUiColumnIndex(final GridData.SelectedCell origin) {
int maxUiColumnIndex = ColumnIndexUtilities.findUiColumnIndex(gridModel.getColumns(),
origin.getColumnIndex());
final List<GridData.SelectedCell> selectedCells = gridModel.getSelectedCells();
for (GridData.SelectedCell selectedCell : selectedCells) {
maxUiColumnIndex = Math.max(ColumnIndexUtilities.findUiColumnIndex(gridModel.getColumns(),
selectedCell.getColumnIndex()),
maxUiColumnIndex);
}
return maxUiColumnIndex;
}
private boolean moveSelection(final GridData.SelectedCell origin,
final SelectionExtension direction) {
final int dx = direction.getDeltaX();
final int dy = direction.getDeltaY();
final int currentUiRowIndex = origin.getRowIndex();
final int currentUiColumnIndex = ColumnIndexUtilities.findUiColumnIndex(gridModel.getColumns(),
origin.getColumnIndex());
final int proposedUiRowIndex = currentUiRowIndex + dy;
final int proposedUiColumnIndex = currentUiColumnIndex + dx;
if (!isCoordinateWithinExtents(proposedUiRowIndex,
proposedUiColumnIndex)) {
return false;
}
return selectCell(proposedUiRowIndex,
proposedUiColumnIndex,
false,
false);
}
private boolean isCoordinateWithinExtents(final int proposedUiRowIndex,
final int proposedUiColumnIndex) {
if (proposedUiRowIndex < 0 || proposedUiRowIndex > gridModel.getRowCount() - 1) {
return false;
}
if (proposedUiColumnIndex < 0 || proposedUiColumnIndex > gridModel.getColumnCount() - 1) {
return false;
}
return true;
}
@Override
public boolean startEditingCell(final Point2D ap) {
//Get row information
final Integer uiRowIndex = CoordinateUtilities.getUiRowIndex(gridWidget,
ap.getY());
if (uiRowIndex == null) {
return false;
}
//Get column information
final BaseGridRendererHelper rendererHelper = gridWidget.getRendererHelper();
final BaseGridRendererHelper.ColumnInformation ci = rendererHelper.getColumnInformation(ap.getX());
final GridColumn<?> column = ci.getColumn();
if (column == null) {
return false;
}
return edit(uiRowIndex,
ci);
}
@Override
public boolean startEditingCell(final int uiRowIndex,
final int uiColumnIndex) {
if (!isCoordinateWithinExtents(uiRowIndex,
uiColumnIndex)) {
return false;
}
BaseGridRendererHelper.ColumnInformation ci = getFloatingColumnInformation(uiColumnIndex);
if (ci == null) {
ci = getBodyColumnInformation(uiColumnIndex);
}
if (ci == null) {
return false;
}
return edit(uiRowIndex,
ci);
}
private BaseGridRendererHelper.ColumnInformation getFloatingColumnInformation(final int uiColumnIndex) {
final GridColumn<?> column = gridModel.getColumns().get(uiColumnIndex);
final BaseGridRendererHelper rendererHelper = gridWidget.getRendererHelper();
final BaseGridRendererHelper.RenderingInformation renderingInformation = rendererHelper.getRenderingInformation();
final BaseGridRendererHelper.RenderingBlockInformation floatingBlockInformation = renderingInformation.getFloatingBlockInformation();
final List<GridColumn<?>> floatingColumns = floatingBlockInformation.getColumns();
if (!floatingColumns.contains(column)) {
return null;
}
return new BaseGridRendererHelper.ColumnInformation(column,
uiColumnIndex,
floatingBlockInformation.getX() + rendererHelper.getColumnOffset(floatingColumns,
floatingColumns.indexOf(column)));
}
private BaseGridRendererHelper.ColumnInformation getBodyColumnInformation(final int uiColumnIndex) {
final GridColumn<?> column = gridModel.getColumns().get(uiColumnIndex);
final BaseGridRendererHelper rendererHelper = gridWidget.getRendererHelper();
final BaseGridRendererHelper.RenderingInformation renderingInformation = rendererHelper.getRenderingInformation();
final BaseGridRendererHelper.RenderingBlockInformation bodyBlockInformation = renderingInformation.getBodyBlockInformation();
final List<GridColumn<?>> bodyColumns = bodyBlockInformation.getColumns();
if (!bodyColumns.contains(column)) {
return null;
}
return new BaseGridRendererHelper.ColumnInformation(column,
uiColumnIndex,
bodyBlockInformation.getX() + rendererHelper.getColumnOffset(bodyColumns,
bodyColumns.indexOf(column)));
}
private boolean edit(final int uiRowIndex,
final BaseGridRendererHelper.ColumnInformation ci) {
final GridColumn<?> column = ci.getColumn();
final int uiColumnIndex = ci.getUiColumnIndex();
final double offsetX = ci.getOffsetX();
//Get rendering information
final GridRenderer renderer = gridWidget.getRenderer();
final BaseGridRendererHelper rendererHelper = gridWidget.getRendererHelper();
final BaseGridRendererHelper.RenderingInformation renderingInformation = rendererHelper.getRenderingInformation();
if (renderingInformation == null) {
return false;
}
final BaseGridRendererHelper.RenderingBlockInformation floatingBlockInformation = renderingInformation.getFloatingBlockInformation();
final double floatingX = floatingBlockInformation.getX();
final double floatingWidth = floatingBlockInformation.getWidth();
//Construct context of MouseEvent
final double cellX = gridWidget.getX() + offsetX;
final double cellY = gridWidget.getY() + renderer.getHeaderHeight() + getRowOffset(uiRowIndex,
uiColumnIndex,
rendererHelper);
final double cellHeight = getCellHeight(uiRowIndex,
uiColumnIndex);
final Group header = gridWidget.getHeader();
final double clipMinY = gridWidget.getY() + header.getY() + renderer.getHeaderHeight();
final double clipMinX = gridWidget.getX() + floatingX + floatingWidth;
final GridBodyCellRenderContext context = new GridBodyCellRenderContext(cellX,
cellY,
column.getWidth(),
cellHeight,
clipMinY,
clipMinX,
uiRowIndex,
uiColumnIndex,
floatingBlockInformation.getColumns().contains(column),
gridWidget.getViewport().getTransform(),
renderer);
doEdit(context);
return true;
}
private double getRowOffset(final int uiRowIndex,
final int uiColumnIndex,
final BaseGridRendererHelper rendererHelper) {
final GridCell<?> cell = gridModel.getCell(uiRowIndex,
uiColumnIndex);
if (cell == null) {
return rendererHelper.getRowOffset(uiRowIndex);
}
if (cell.getMergedCellCount() == 1) {
return rendererHelper.getRowOffset(uiRowIndex);
} else if (cell.getMergedCellCount() > 1) {
return rendererHelper.getRowOffset(uiRowIndex);
} else {
int _uiRowIndex = uiRowIndex;
GridCell<?> _cell = cell;
while (_cell.getMergedCellCount() == 0) {
_uiRowIndex--;
_cell = gridModel.getCell(_uiRowIndex,
uiColumnIndex);
}
return rendererHelper.getRowOffset(_uiRowIndex);
}
}
private double getCellHeight(final int uiRowIndex,
final int uiColumnIndex) {
final GridCell<?> cell = gridModel.getCell(uiRowIndex,
uiColumnIndex);
if (cell == null) {
return gridModel.getRow(uiRowIndex).getHeight();
}
if (cell.getMergedCellCount() == 1) {
return gridModel.getRow(uiRowIndex).getHeight();
} else if (cell.getMergedCellCount() > 1) {
return getMergedCellHeight(uiRowIndex,
uiColumnIndex);
} else {
return getClippedMergedCellHeight(uiRowIndex,
uiColumnIndex);
}
}
private double getMergedCellHeight(final int uiRowIndex,
final int uiColumnIndex) {
double height = 0;
final GridCell<?> cell = gridModel.getCell(uiRowIndex,
uiColumnIndex);
for (int i = uiRowIndex; i < uiRowIndex + cell.getMergedCellCount(); i++) {
height = height + gridModel.getRow(i).getHeight();
}
return height;
}
private double getClippedMergedCellHeight(final int uiRowIndex,
final int uiColumnIndex) {
final GridCell<?> cell = gridModel.getCell(uiRowIndex,
uiColumnIndex);
GridCell<?> _cell = cell;
int _uiRowIndex = uiRowIndex;
while (_cell.getMergedCellCount() == 0) {
_uiRowIndex--;
_cell = gridModel.getCell(_uiRowIndex,
uiColumnIndex);
}
double height = 0;
for (int i = _uiRowIndex; i < _uiRowIndex + _cell.getMergedCellCount(); i++) {
height = height + gridModel.getRow(i).getHeight();
}
return height;
}
@SuppressWarnings("unchecked")
protected void doEdit(final GridBodyCellRenderContext context) {
final int uiRowIndex = context.getRowIndex();
final int uiColumnIndex = context.getColumnIndex();
final GridData gridModel = gridWidget.getModel();
final GridColumn column = gridModel.getColumns().get(uiColumnIndex);
final GridCell<?> cell = gridModel.getCell(uiRowIndex,
uiColumnIndex);
column.edit(cell,
context,
new Callback<GridCellValue<?>>() {
@Override
public void callback(final GridCellValue<?> value) {
gridModel.setCell(uiRowIndex,
uiColumnIndex,
value);
gridWidget.getLayer().batch();
}
});
}
}