blob: 08239bb60aab76de28a12a060a75a266cbe3b43c [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2019 Stephan Wahlbrink and others.
#
# 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, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.r.ui.intable;
import static org.eclipse.statet.ecommons.waltable.coordinate.Orientation.HORIZONTAL;
import static org.eclipse.statet.ecommons.waltable.coordinate.Orientation.VERTICAL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.statet.ecommons.waltable.config.LayoutSizeConfig;
import org.eclipse.statet.ecommons.waltable.coordinate.Orientation;
import org.eclipse.statet.ecommons.waltable.coordinate.PositionId;
import org.eclipse.statet.ecommons.waltable.coordinate.PositionOutOfBoundsException;
import org.eclipse.statet.ecommons.waltable.layer.AbstractLayer;
import org.eclipse.statet.ecommons.waltable.layer.DataDim;
import org.eclipse.statet.ecommons.waltable.layer.ILayer;
import org.eclipse.statet.ecommons.waltable.layer.ILayerDim;
import org.eclipse.statet.ecommons.waltable.layer.cell.ILayerCell;
import org.eclipse.statet.ecommons.waltable.layer.cell.LayerCell;
import org.eclipse.statet.ecommons.waltable.layer.cell.LayerCellDim;
import org.eclipse.statet.ecommons.waltable.resize.ColumnResizeEvent;
import org.eclipse.statet.internal.r.ui.dataeditor.AbstractRDataProvider;
import org.eclipse.statet.internal.r.ui.dataeditor.RDataFormatter;
import org.eclipse.statet.internal.r.ui.dataeditor.RDataTableContentDescription;
import org.eclipse.statet.r.ui.dataeditor.RDataTableColumn;
public class RDataLayer extends AbstractLayer {
private static int minmax(final int value, final int min, final int max) {
return (value <= min) ? min : ((value >= max) ? max : value);
}
private static class ColumnsDim extends DataDim<RDataLayer> {
private final int defaultMinChars= 8;
private final int defaultMaxChars= 20;
private final int autoMinChars= 3;
private final int autoMaxChars= 1000;
private final int fallbackPositionSize= 10;
private final Map<Long, Integer> customPositionSizes= new HashMap<>();
private int charSize;
private int spacing;
public ColumnsDim(final RDataLayer layer, final int charSize, final int spacing) {
super(layer, Orientation.HORIZONTAL, PositionId.BODY_CAT);
this.charSize= charSize;
this.spacing= spacing;
}
@Override
public long getPositionCount() {
return this.layer.dataProvider.getColumnCount();
}
public void setSize(final int charSize, final int spacing) {
if (charSize != this.charSize || spacing != this.spacing) {
final double factor= (double) charSize / this.charSize;
for (final Map.Entry<Long, Integer> entry : this.customPositionSizes.entrySet()) {
entry.setValue((int) (
(entry.getValue() - this.spacing) * factor + spacing + 0.5 ));
}
}
this.charSize= charSize;
this.spacing= spacing;
}
private long aggregateSize(final long position) {
if (position < 0) {
return -1;
}
if (this.layer.dataProvider.getAllColumnsEqual()) {
final Integer columnWidth= this.customPositionSizes.get(Long.valueOf(0));
if (columnWidth != null) {
return columnWidth * position;
}
else {
return getDefaultSize(0) * position;
}
}
long width= 0;
for (long i= 0; i < position; i++) {
final Integer columnWidth= this.customPositionSizes.get(Long.valueOf(i));
if (columnWidth != null) {
width += columnWidth.intValue();
}
else {
width += getDefaultSize(i);
}
}
return width;
}
@Override
public long getSize() {
return aggregateSize(getPositionCount());
}
@Override
public long getPositionStart(final long position) {
if (position < 0 || position >= getPositionCount()) {
throw PositionOutOfBoundsException.position(position, getOrientation());
}
return aggregateSize(position);
}
@Override
public int getPositionSize(final long position) {
if (position < 0 || position >= getPositionCount()) {
throw PositionOutOfBoundsException.position(position, getOrientation());
}
final Integer columnWidth= this.customPositionSizes.get(
this.layer.dataProvider.getAllColumnsEqual() ?
Long.valueOf(0) : Long.valueOf(position));
if (columnWidth != null) {
return columnWidth.intValue();
}
return getDefaultSize(position);
}
@Override
public boolean isPositionResizable(final long position) {
return true;
}
protected int getFormatterCharWidth(final long position) {
RDataFormatter formatter;
if (this.layer.dataProvider.getAllColumnsEqual()) {
final RDataTableContentDescription description= this.layer.dataProvider.getDescription();
if (description != null
&& (formatter= description.getDefaultDataFormat()) != null
&& formatter.getAutoWidth() >= 0) {
return formatter.getAutoWidth();
}
return this.fallbackPositionSize;
}
else {
final RDataTableContentDescription description= this.layer.dataProvider.getDescription();
if (description != null) {
final List<RDataTableColumn> columns= description.getDataColumns();
if (position < columns.size()
&& (formatter= columns.get((int) position).getDefaultFormat()) != null
&& formatter.getAutoWidth() >= 0) {
return formatter.getAutoWidth();
}
if ((formatter= description.getDefaultDataFormat()) != null
&& formatter.getAutoWidth() >= 0) {
return formatter.getAutoWidth();
}
}
return this.fallbackPositionSize;
}
}
protected int getDefaultSize(final long position) {
final int charWidth= minmax(getFormatterCharWidth(position),
this.defaultMinChars, this.defaultMaxChars );
return charWidth * this.charSize + this.spacing;
}
protected int getAutoSize(final long position) {
final int charWidth= minmax(getFormatterCharWidth(position),
this.autoMinChars, this.autoMaxChars );
return charWidth * this.charSize + this.spacing;
}
public void setCustomSize(final long position, final int size) {
this.customPositionSizes.put(
this.layer.dataProvider.getAllColumnsEqual() ?
Long.valueOf(0) : Long.valueOf(position),
size );
}
public void setAutoSize(final long position) {
this.customPositionSizes.put(
this.layer.dataProvider.getAllColumnsEqual() ?
Long.valueOf(0) : Long.valueOf(position),
getAutoSize(position) );
}
}
private static class RowsDim extends DataDim<RDataLayer> {
private int positionSize;
public RowsDim(final RDataLayer layer, final int positionSize) {
super(layer, Orientation.VERTICAL, PositionId.BODY_CAT);
this.positionSize= positionSize;
}
@Override
public long getPositionCount() {
return this.layer.dataProvider.getRowCount();
}
public void setSize(final int rowHeight) {
this.positionSize= rowHeight;
}
@Override
public long getSize() {
return getPositionCount() * this.positionSize;
}
@Override
public long getPositionStart(final long position) {
if (position < 0 || position >= getPositionCount()) {
throw new IndexOutOfBoundsException("position: " + position); //$NON-NLS-1$
}
return position * this.positionSize;
}
@Override
public int getPositionSize(final long position) {
if (position < 0 || position >= getPositionCount()) {
throw new IndexOutOfBoundsException("position: " + position); //$NON-NLS-1$
}
return this.positionSize;
}
@Override
public boolean isPositionResizable(final long position) {
return false;
}
}
private final AbstractRDataProvider<?> dataProvider;
private LayoutSizeConfig sizeConfig;
public RDataLayer(final AbstractRDataProvider<?> dataProvider, final LayoutSizeConfig sizeConfig) {
this.dataProvider= dataProvider;
this.sizeConfig= sizeConfig;
initDims();
registerCommandHandlers();
}
@Override
protected void initDims() {
if (this.sizeConfig == null) {
return;
}
setDim(new ColumnsDim(this,
this.sizeConfig.getCharWidth(), this.sizeConfig.getDefaultSpace() * 4 ));
setDim(new RowsDim(this,
this.sizeConfig.getRowHeight() ));
}
private ColumnsDim getColumnDim() {
return (ColumnsDim) getDim(HORIZONTAL);
}
private RowsDim getRowDim() {
return (RowsDim) getDim(VERTICAL);
}
public void setSizeConfig(final LayoutSizeConfig sizeConfig) {
this.sizeConfig= sizeConfig;
getColumnDim().setSize(sizeConfig.getCharWidth(), this.sizeConfig.getDefaultSpace() * 4);
getRowDim().setSize(sizeConfig.getRowHeight());
}
// Configuration
@Override
protected void registerCommandHandlers() {
registerCommandHandler(new DimPositionResizeCommandHandler(this));
registerCommandHandler(new MultiColumnResizeCommandHandler(this));
registerCommandHandler(new MultiColumnAutoResizeCommandHandler(this));
// registerCommandHandler(new UpdateDataCommandHandler(this));
}
// Columns
public void setColumnWidth(final long columnPosition, final int width) {
getColumnDim().setCustomSize(columnPosition, width);
fireLayerEvent(new ColumnResizeEvent(this, columnPosition));
}
public void setColumnWidthToAutoWidth(final long columnPosition) {
getColumnDim().setAutoSize(columnPosition);
fireLayerEvent(new ColumnResizeEvent(this, columnPosition));
}
// Cell features
@Override
public ILayerCell getCellByPosition(final long columnPosition, final long rowPosition) {
final ILayerDim hDim= getDim(HORIZONTAL);
final ILayerDim vDim= getDim(VERTICAL);
final long columnId= hDim.getPositionId(columnPosition, columnPosition);
final long rowId= vDim.getPositionId(rowPosition, rowPosition);
return new LayerCell(this,
new LayerCellDim(HORIZONTAL, columnId, columnPosition),
new LayerCellDim(VERTICAL, rowId, rowPosition) ) {
@Override
public Object getDataValue(final int flags, final IProgressMonitor monitor) {
return RDataLayer.this.dataProvider.getDataValue(getColumnPosition(), getRowPosition(),
flags, monitor );
}
};
}
@Override
public ILayer getUnderlyingLayerByPosition(final long columnPosition, final long rowPosition) {
return null;
}
}