/******************************************************************************* | |
* Copyright (c) 2012 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 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; | |
public abstract class AbstractColumnHideShowLayer extends AbstractLayerTransform implements IUniqueIndexLayer { | |
private List<Integer> cachedVisibleColumnIndexOrder; | |
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 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); | |
if (columnIndex != null) { | |
return columnIndex.intValue(); | |
} else { | |
return -1; | |
} | |
} | |
public int getColumnPositionByIndex(int columnIndex) { | |
return getCachedVisibleColumnIndexes().indexOf(Integer.valueOf(columnIndex)); | |
} | |
public Collection<Integer> getColumnPositionsByIndexes(int[] columnIndexes) { | |
Collection<Integer> columnPositions = new HashSet<Integer>(); | |
for (int columnIndex : columnIndexes) { | |
columnPositions.add(Integer.valueOf(getColumnPositionByIndex(columnIndex))); | |
} | |
return columnPositions; | |
} | |
public Collection<Integer> getColumnPositionsByIndexes(List<Integer> columnIndexes) { | |
int[] indexArray = new int[columnIndexes.size()]; | |
for (int i = 0; i < columnIndexes.size(); i++) { | |
indexArray[i] = columnIndexes.get(i); | |
} | |
return getColumnPositionsByIndexes(indexArray); | |
} | |
@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 = cachedHiddenColumnIndexToPositionMap.get(Integer.valueOf(columnIndex)); | |
if (hiddenColumnPosition != null) { | |
return hiddenColumnPosition.intValue(); | |
} else { | |
return -1; | |
} | |
} | |
} | |
@Override | |
public Collection<Range> underlyingToLocalColumnPositions(ILayer sourceUnderlyingLayer, Collection<Range> underlyingColumnPositionRanges) { | |
Collection<Range> localColumnPositionRanges = new ArrayList<Range>(); | |
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() { | |
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 = startXCache.get(Integer.valueOf(localColumnPosition)); | |
if (cachedStartX != null) { | |
return cachedStartX.intValue(); | |
} | |
IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer(); | |
int underlyingPosition = localToUnderlyingColumnPosition(localColumnPosition); | |
int underlyingStartX = underlyingLayer.getStartXOfColumnPosition(underlyingPosition); | |
if (underlyingStartX < 0) { | |
return -1; | |
} | |
for (Integer hiddenIndex : getHiddenColumnIndexes()) { | |
int hiddenPosition = underlyingLayer.getColumnPositionByIndex(hiddenIndex.intValue()); | |
if (hiddenPosition <= underlyingPosition) { | |
underlyingStartX -= underlyingLayer.getColumnWidthByPosition(hiddenPosition); | |
} | |
} | |
startXCache.put(Integer.valueOf(localColumnPosition), Integer.valueOf(underlyingStartX)); | |
return underlyingStartX; | |
} | |
// Vertical features | |
// Rows | |
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() { | |
cachedVisibleColumnIndexOrder = null; | |
startXCache.clear(); | |
} | |
private List<Integer> getCachedVisibleColumnIndexes() { | |
if (cachedVisibleColumnIndexOrder == null) { | |
cacheVisibleColumnIndexes(); | |
} | |
return cachedVisibleColumnIndexOrder; | |
} | |
private void cacheVisibleColumnIndexes() { | |
cachedVisibleColumnIndexOrder = new ArrayList<Integer>(); | |
cachedHiddenColumnIndexToPositionMap = new HashMap<Integer, Integer>(); | |
startXCache.clear(); | |
ILayer underlyingLayer = getUnderlyingLayer(); | |
int columnPosition = 0; | |
for (int parentColumnPosition = 0; parentColumnPosition < underlyingLayer.getColumnCount(); parentColumnPosition++) { | |
int columnIndex = underlyingLayer.getColumnIndexByPosition(parentColumnPosition); | |
if (!isColumnIndexHidden(columnIndex)) { | |
cachedVisibleColumnIndexOrder.add(Integer.valueOf(columnIndex)); | |
columnPosition++; | |
} else { | |
cachedHiddenColumnIndexToPositionMap.put(Integer.valueOf(columnIndex), Integer.valueOf(columnPosition)); | |
} | |
} | |
} | |
} |