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