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