blob: 8cfe0bc352173c84708695d649c2554539b9a26d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2019 Original authors and others.
* 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:
* Original authors and others - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.hideshow;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
import org.eclipse.nebula.widgets.nattable.coordinate.Range;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel.ColumnGroup;
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent;
import org.eclipse.nebula.widgets.nattable.reorder.event.ColumnReorderEvent;
public abstract class AbstractColumnHideShowLayer extends AbstractLayerTransform implements IUniqueIndexLayer {
private List<Integer> cachedVisibleColumnIndexOrder;
private Map<Integer, Integer> cachedVisibleColumnIndexPositionMap;
private Map<Integer, Integer> cachedHiddenColumnIndexToPositionMap;
private final Map<Integer, Integer> startXCache = new HashMap<Integer, Integer>();
public AbstractColumnHideShowLayer(IUniqueIndexLayer underlyingLayer) {
super(underlyingLayer);
}
@Override
public void handleLayerEvent(ILayerEvent event) {
if (event instanceof ColumnReorderEvent) {
// we need to convert the before positions in the event BEFORE the
// local states are changed, otherwise we are not able to convert
// the before positions as the changed layer states would return
// incorrect values
ColumnReorderEvent reorderEvent = (ColumnReorderEvent) event;
Collection<Integer> fromPositions = new TreeSet<Integer>();
for (int pos : reorderEvent.getBeforeFromColumnIndexes()) {
fromPositions.add(getColumnPositionByIndex(pos));
}
Collection<Range> fromRanges = PositionUtil.getRanges(fromPositions);
int pos = -1;
if (!isColumnIndexHidden(reorderEvent.getBeforeToColumnIndex())) {
pos = getColumnPositionByIndex(reorderEvent.getBeforeToColumnIndex());
} else {
int i = 1;
while (pos < 0) {
int next = reorderEvent.getBeforeToColumnPosition() + i;
pos = underlyingToLocalColumnPosition(this.underlyingLayer, next);
}
reorderEvent.setBeforeToColumnIndex(getColumnIndexByPosition(pos));
}
reorderEvent.setConvertedBeforePositions(this, fromRanges, pos);
}
if (event instanceof IStructuralChangeEvent) {
IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event;
if (structuralChangeEvent.isHorizontalStructureChanged()) {
invalidateCache();
}
}
super.handleLayerEvent(event);
}
// Horizontal features
// Columns
@Override
public int getColumnCount() {
return getCachedVisibleColumnIndexes().size();
}
@Override
public int getColumnIndexByPosition(int columnPosition) {
if (columnPosition < 0 || columnPosition >= getColumnCount()) {
return -1;
}
Integer columnIndex = getCachedVisibleColumnIndexes().get(columnPosition);
return (columnIndex != null) ? columnIndex : -1;
}
@Override
public int getColumnPositionByIndex(int columnIndex) {
if (this.cachedVisibleColumnIndexPositionMap == null) {
cacheVisibleColumnIndexes();
}
Integer position = this.cachedVisibleColumnIndexPositionMap.get(columnIndex);
return (position != null) ? position : -1;
}
public Collection<Integer> getColumnPositionsByIndexes(Collection<Integer> columnIndexes) {
Collection<Integer> columnPositions = new HashSet<Integer>();
for (int columnIndex : columnIndexes) {
columnPositions.add(getColumnPositionByIndex(columnIndex));
}
return columnPositions;
}
@Override
public int localToUnderlyingColumnPosition(int localColumnPosition) {
int columnIndex = getColumnIndexByPosition(localColumnPosition);
return ((IUniqueIndexLayer) getUnderlyingLayer()).getColumnPositionByIndex(columnIndex);
}
@Override
public int underlyingToLocalColumnPosition(ILayer sourceUnderlyingLayer, int underlyingColumnPosition) {
int columnIndex = getUnderlyingLayer().getColumnIndexByPosition(underlyingColumnPosition);
int columnPosition = getColumnPositionByIndex(columnIndex);
if (columnPosition >= 0) {
return columnPosition;
} else {
Integer hiddenColumnPosition = this.cachedHiddenColumnIndexToPositionMap.get(columnIndex);
return (hiddenColumnPosition != null) ? hiddenColumnPosition : -1;
}
}
@Override
public Collection<Range> underlyingToLocalColumnPositions(
ILayer sourceUnderlyingLayer,
Collection<Range> underlyingColumnPositionRanges) {
Collection<Range> localColumnPositionRanges =
new ArrayList<Range>(underlyingColumnPositionRanges.size());
for (Range underlyingColumnPositionRange : underlyingColumnPositionRanges) {
int startColumnPosition = getAdjustedUnderlyingToLocalStartPosition(
sourceUnderlyingLayer,
underlyingColumnPositionRange.start,
underlyingColumnPositionRange.end);
int endColumnPosition = getAdjustedUnderlyingToLocalEndPosition(
sourceUnderlyingLayer,
underlyingColumnPositionRange.end,
underlyingColumnPositionRange.start);
// teichstaedt: fixes the problem that ranges where added even if
// the corresponding startPosition weren't found in the underlying
// layer. Without that fix a bunch of ranges of kind Range [-1, 180]
// which causes strange behaviour in Freeze- and other Layers were
// returned.
if (startColumnPosition > -1) {
localColumnPositionRanges.add(
new Range(startColumnPosition, endColumnPosition));
}
}
return localColumnPositionRanges;
}
private int getAdjustedUnderlyingToLocalStartPosition(
ILayer sourceUnderlyingLayer,
int startUnderlyingPosition,
int endUnderlyingPosition) {
int localStartColumnPosition =
underlyingToLocalColumnPosition(
sourceUnderlyingLayer,
startUnderlyingPosition);
int offset = 0;
while (localStartColumnPosition < 0
&& (startUnderlyingPosition + offset < endUnderlyingPosition)) {
localStartColumnPosition =
underlyingToLocalColumnPosition(
sourceUnderlyingLayer,
startUnderlyingPosition + offset++);
}
return localStartColumnPosition;
}
private int getAdjustedUnderlyingToLocalEndPosition(
ILayer sourceUnderlyingLayer,
int endUnderlyingPosition,
int startUnderlyingPosition) {
int localEndColumnPosition =
underlyingToLocalColumnPosition(
sourceUnderlyingLayer,
endUnderlyingPosition - 1);
int offset = 0;
while (localEndColumnPosition < 0
&& (endUnderlyingPosition - offset > startUnderlyingPosition)) {
localEndColumnPosition =
underlyingToLocalColumnPosition(
sourceUnderlyingLayer,
endUnderlyingPosition - offset++);
}
return localEndColumnPosition + 1;
}
// Width
@Override
public int getWidth() {
if (getColumnCount() == 0) {
return 0;
}
int lastColumnPosition = getColumnCount() - 1;
return getStartXOfColumnPosition(lastColumnPosition)
+ getColumnWidthByPosition(lastColumnPosition);
}
// X
@Override
public int getColumnPositionByX(int x) {
return LayerUtil.getColumnPositionByX(this, x);
}
@Override
public int getStartXOfColumnPosition(int localColumnPosition) {
Integer cachedStartX = this.startXCache.get(localColumnPosition);
if (cachedStartX != null) {
return cachedStartX.intValue();
}
IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();
int underlyingPosition = localToUnderlyingColumnPosition(localColumnPosition);
if (underlyingPosition < 0) {
return -1;
}
int underlyingStartX = underlyingLayer.getStartXOfColumnPosition(underlyingPosition);
if (underlyingStartX < 0) {
return -1;
}
for (Integer hiddenIndex : getHiddenColumnIndexes()) {
int hiddenPosition = underlyingLayer.getColumnPositionByIndex(hiddenIndex);
if (hiddenPosition <= underlyingPosition) {
underlyingStartX -= underlyingLayer.getColumnWidthByPosition(hiddenPosition);
}
}
this.startXCache.put(localColumnPosition, underlyingStartX);
return underlyingStartX;
}
// Vertical features
// Rows
@Override
public int getRowPositionByIndex(int rowIndex) {
return ((IUniqueIndexLayer) getUnderlyingLayer()).getRowPositionByIndex(rowIndex);
}
// Hide/show
/**
* Will check if the column at the specified index is hidden or not. Checks
* this layer and also the sublayers for the visibility. Note: As the
* {@link ColumnGroup}s are created index based, this method only works
* correctly with indexes rather than positions.
*
* @param columnIndex
* The column index of the column whose visibility state should
* be checked.
* @return <code>true</code> if the column at the specified index is hidden,
* <code>false</code> if it is visible.
*/
public abstract boolean isColumnIndexHidden(int columnIndex);
/**
* Will collect and return all indexes of the columns that are hidden in
* this layer. Note: It is not intended that it also collects the column
* indexes of underlying layers. This would cause issues on calculating
* positions as every layer is responsible for those calculations itself.
*
* @return Collection of all column indexes that are hidden in this layer.
*/
public abstract Collection<Integer> getHiddenColumnIndexes();
// Cache
/**
* Invalidate the cache to ensure that information is rebuild.
*/
protected void invalidateCache() {
this.cachedVisibleColumnIndexOrder = null;
this.cachedVisibleColumnIndexPositionMap = null;
this.startXCache.clear();
}
private List<Integer> getCachedVisibleColumnIndexes() {
if (this.cachedVisibleColumnIndexOrder == null) {
cacheVisibleColumnIndexes();
}
return this.cachedVisibleColumnIndexOrder;
}
private void cacheVisibleColumnIndexes() {
this.cachedVisibleColumnIndexOrder = new ArrayList<Integer>();
this.cachedVisibleColumnIndexPositionMap = new HashMap<Integer, Integer>();
this.cachedHiddenColumnIndexToPositionMap = new HashMap<Integer, Integer>();
this.startXCache.clear();
ILayer underlyingLayer = getUnderlyingLayer();
int columnPosition = 0;
for (int parentColumnPosition = 0; parentColumnPosition < underlyingLayer.getColumnCount(); parentColumnPosition++) {
int columnIndex = underlyingLayer.getColumnIndexByPosition(parentColumnPosition);
if (!isColumnIndexHidden(columnIndex)) {
this.cachedVisibleColumnIndexOrder.add(columnIndex);
this.cachedVisibleColumnIndexPositionMap.put(columnIndex, columnPosition);
columnPosition++;
} else {
this.cachedHiddenColumnIndexToPositionMap.put(columnIndex, columnPosition);
}
}
}
}