--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2012 Original authors and others.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ * \r
+ * Contributors:\r
+ * Original authors and others - initial API and implementation\r
+ ******************************************************************************/\r
+package org.simantics.browsing.ui.nattable.override;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+\r
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;\r
+import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;\r
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.VisualRefreshEvent;\r
+\r
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;\r
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;\r
+\r
+/**\r
+ * AbstractRowHideShowLayer implementation with FastUtils hashmaps.\r
+ * \r
+ * @see org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer\r
+ * \r
+ * @author MLMARKO\r
+ *\r
+ */\r
+public abstract class AbstractRowHideShowLayer2 extends AbstractLayerTransform implements IUniqueIndexLayer {\r
+\r
+ private Int2IntOpenHashMap cachedVisibleRowIndexOrder;\r
+ private Int2IntOpenHashMap cachedVisibleRowPositionOrder;\r
+ \r
+ private Int2IntOpenHashMap cachedHiddenRowIndexToPositionMap;\r
+\r
+ protected final Int2IntOpenHashMap startYCache = new Int2IntOpenHashMap(); \r
+ \r
+ \r
+ public AbstractRowHideShowLayer2(IUniqueIndexLayer underlyingLayer) {\r
+ super(underlyingLayer);\r
+ }\r
+ \r
+ @Override\r
+ public void handleLayerEvent(ILayerEvent event) {\r
+ if (event instanceof IStructuralChangeEvent) {\r
+ IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event;\r
+ if (structuralChangeEvent.isVerticalStructureChanged()) {\r
+ // vertical structure has changed, update cached row information\r
+ invalidateCache();\r
+ }\r
+ } else if (event instanceof VisualRefreshEvent) {\r
+ // visual change, e.g. font change, the startYCache needs to be\r
+ // cleared in order to re-render correctly\r
+ this.startYCache.clear();\r
+ }\r
+ super.handleLayerEvent(event);\r
+ }\r
+\r
+ // Horizontal features\r
+\r
+ // Columns\r
+\r
+ @Override\r
+ public int getColumnPositionByIndex(int columnIndex) {\r
+ return ((IUniqueIndexLayer) getUnderlyingLayer()).getColumnPositionByIndex(columnIndex);\r
+ }\r
+\r
+ // Vertical features\r
+\r
+ // Rows\r
+\r
+ @Override\r
+ public int getRowCount() {\r
+ return getCachedVisibleRowIndexes().size();\r
+ }\r
+\r
+ @Override\r
+ public int getRowIndexByPosition(int rowPosition) {\r
+ if (rowPosition < 0 || rowPosition >= getRowCount()) {\r
+ return -1;\r
+ }\r
+ return getCachedVisibleRowPositons().get(rowPosition);\r
+ }\r
+\r
+ @Override\r
+ public int getRowPositionByIndex(int rowIndex) {\r
+ return getCachedVisibleRowIndexes().get(rowIndex);\r
+ }\r
+\r
+ public Collection<Integer> getRowPositionsByIndexes(Collection<Integer> rowIndexes) {\r
+ IntOpenHashSet rowPositions = new IntOpenHashSet();\r
+ for (int rowIndex : rowIndexes) {\r
+ rowPositions.add(getRowPositionByIndex(rowIndex));\r
+ }\r
+ return rowPositions;\r
+ }\r
+\r
+ @Override\r
+ public int localToUnderlyingRowPosition(int localRowPosition) {\r
+ int rowIndex = getRowIndexByPosition(localRowPosition);\r
+ return ((IUniqueIndexLayer) getUnderlyingLayer()).getRowPositionByIndex(rowIndex);\r
+ }\r
+\r
+ @Override\r
+ public int underlyingToLocalRowPosition(ILayer sourceUnderlyingLayer, int underlyingRowPosition) {\r
+ int rowIndex = getUnderlyingLayer().getRowIndexByPosition(underlyingRowPosition);\r
+ int rowPosition = getRowPositionByIndex(rowIndex);\r
+ if (rowPosition >= 0) {\r
+ return rowPosition;\r
+ } else {\r
+ if (this.cachedHiddenRowIndexToPositionMap.containsKey(rowIndex)) {\r
+ return this.cachedHiddenRowIndexToPositionMap.get(rowIndex);\r
+ } else {\r
+ return -1;\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Collection<Range> underlyingToLocalRowPositions(\r
+ ILayer sourceUnderlyingLayer, Collection<Range> underlyingRowPositionRanges) {\r
+ Collection<Range> localRowPositionRanges = new ArrayList<Range>();\r
+\r
+ for (Range underlyingRowPositionRange : underlyingRowPositionRanges) {\r
+ int startRowPosition = getAdjustedUnderlyingToLocalStartPosition(\r
+ sourceUnderlyingLayer,\r
+ underlyingRowPositionRange.start,\r
+ underlyingRowPositionRange.end);\r
+ int endRowPosition = getAdjustedUnderlyingToLocalEndPosition(\r
+ sourceUnderlyingLayer,\r
+ underlyingRowPositionRange.end,\r
+ underlyingRowPositionRange.start);\r
+\r
+ // teichstaedt: fixes the problem that ranges where added even if\r
+ // the corresponding startPosition weren't found in the underlying\r
+ // layer. Without that fix a bunch of ranges of kind Range [-1, 180]\r
+ // which causes strange behaviour in Freeze- and other Layers were\r
+ // returned.\r
+ if (startRowPosition > -1) {\r
+ localRowPositionRanges.add(new Range(startRowPosition, endRowPosition));\r
+ }\r
+ }\r
+\r
+ return localRowPositionRanges;\r
+ }\r
+\r
+ private int getAdjustedUnderlyingToLocalStartPosition(\r
+ ILayer sourceUnderlyingLayer,\r
+ int startUnderlyingPosition,\r
+ int endUnderlyingPosition) {\r
+ int localStartRowPosition = underlyingToLocalRowPosition(sourceUnderlyingLayer, startUnderlyingPosition);\r
+ int offset = 0;\r
+ while (localStartRowPosition < 0\r
+ && (startUnderlyingPosition + offset < endUnderlyingPosition)) {\r
+ localStartRowPosition =\r
+ underlyingToLocalRowPosition(sourceUnderlyingLayer, startUnderlyingPosition + offset++);\r
+ }\r
+ return localStartRowPosition;\r
+ }\r
+\r
+ private int getAdjustedUnderlyingToLocalEndPosition(\r
+ ILayer sourceUnderlyingLayer,\r
+ int endUnderlyingPosition,\r
+ int startUnderlyingPosition) {\r
+ int localEndRowPosition = underlyingToLocalRowPosition(sourceUnderlyingLayer, endUnderlyingPosition - 1);\r
+ int offset = 0;\r
+ while (localEndRowPosition < 0\r
+ && (endUnderlyingPosition - offset > startUnderlyingPosition)) {\r
+ localEndRowPosition =\r
+ underlyingToLocalRowPosition(sourceUnderlyingLayer, endUnderlyingPosition - offset++);\r
+ }\r
+ return localEndRowPosition + 1;\r
+ }\r
+\r
+ // Height\r
+\r
+ @Override\r
+ public int getHeight() {\r
+ int lastRowPosition = getRowCount() - 1;\r
+ return getStartYOfRowPosition(lastRowPosition) + getRowHeightByPosition(lastRowPosition);\r
+ }\r
+\r
+ // Y\r
+\r
+ @Override\r
+ public int getRowPositionByY(int y) {\r
+ return LayerUtil.getRowPositionByY(this, y);\r
+ }\r
+\r
+ @Override\r
+ public int getStartYOfRowPosition(int localRowPosition) {\r
+ int index = this.startYCache.get(localRowPosition);\r
+ if (index >= 0)\r
+ return index;\r
+\r
+ IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();\r
+ int underlyingPosition = localToUnderlyingRowPosition(localRowPosition);\r
+ if (underlyingPosition < 0) {\r
+ return -1;\r
+ }\r
+ int underlyingStartY = underlyingLayer.getStartYOfRowPosition(underlyingPosition);\r
+ if (underlyingStartY < 0) {\r
+ return -1;\r
+ }\r
+\r
+ for (Integer hiddenIndex : getHiddenRowIndexes()) {\r
+ int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex);\r
+ // if the hidden position is -1, it is hidden in the underlying\r
+ // layertherefore the underlying layer should handle the positioning\r
+ if (hiddenPosition >= 0 && hiddenPosition <= underlyingPosition) {\r
+ underlyingStartY -= underlyingLayer.getRowHeightByPosition(hiddenPosition);\r
+ }\r
+ }\r
+\r
+ this.startYCache.put(localRowPosition, underlyingStartY);\r
+ return underlyingStartY;\r
+ }\r
+\r
+ // Hide/show\r
+\r
+ /**\r
+ * Will check if the row at the specified index is hidden or not. Checks\r
+ * this layer and also the sublayers for the visibility.\r
+ *\r
+ * @param rowIndex\r
+ * The row index of the row whose visibility state should be\r
+ * checked.\r
+ * @return <code>true</code> if the row at the specified index is hidden,\r
+ * <code>false</code> if it is visible.\r
+ */\r
+ public abstract boolean isRowIndexHidden(int rowIndex);\r
+\r
+ /**\r
+ * Will collect and return all indexes of the rows that are hidden in this\r
+ * layer. Note: It is not intended that it also collects the row indexes of\r
+ * underlying layers. This would cause issues on calculating positions as\r
+ * every layer is responsible for those calculations itself.\r
+ *\r
+ * @return Collection of all row indexes that are hidden in this layer.\r
+ */\r
+ public abstract Collection<Integer> getHiddenRowIndexes();\r
+\r
+ // Cache\r
+\r
+ /**\r
+ * Invalidate the cache to ensure that information is rebuild.\r
+ */\r
+ protected void invalidateCache() {\r
+ this.cachedVisibleRowIndexOrder = null;\r
+ this.cachedVisibleRowPositionOrder = null;\r
+ this.cachedHiddenRowIndexToPositionMap = null;\r
+ this.startYCache.clear();\r
+ }\r
+\r
+ private Int2IntOpenHashMap getCachedVisibleRowIndexes() {\r
+ if (this.cachedVisibleRowIndexOrder == null) {\r
+ cacheVisibleRowIndexes();\r
+ }\r
+ return this.cachedVisibleRowIndexOrder;\r
+ }\r
+\r
+ private Int2IntOpenHashMap getCachedVisibleRowPositons() {\r
+ if (this.cachedVisibleRowPositionOrder == null) {\r
+ cacheVisibleRowIndexes();\r
+ }\r
+ return this.cachedVisibleRowPositionOrder;\r
+ }\r
+\r
+ protected void cacheVisibleRowIndexes() {\r
+ this.cachedVisibleRowIndexOrder = new Int2IntOpenHashMap();\r
+ this.cachedVisibleRowPositionOrder = new Int2IntOpenHashMap();\r
+ this.cachedHiddenRowIndexToPositionMap = new Int2IntOpenHashMap();\r
+ this.startYCache.clear();\r
+ \r
+ cachedVisibleRowPositionOrder.defaultReturnValue(-1);\r
+ cachedVisibleRowIndexOrder.defaultReturnValue(-1);\r
+ cachedHiddenRowIndexToPositionMap.defaultReturnValue(-1);\r
+ startYCache.defaultReturnValue(-1);\r
+\r
+ ILayer underlyingLayer = getUnderlyingLayer();\r
+ int rowPosition = 0;\r
+ for (int parentRowPosition = 0; parentRowPosition < underlyingLayer.getRowCount(); parentRowPosition++) {\r
+ int rowIndex = underlyingLayer.getRowIndexByPosition(parentRowPosition);\r
+\r
+ if (!isRowIndexHidden(rowIndex)) {\r
+ this.cachedVisibleRowIndexOrder.put(rowIndex, rowPosition);\r
+ this.cachedVisibleRowPositionOrder.put(rowPosition, rowIndex);\r
+ rowPosition++;\r
+ } else {\r
+ this.cachedHiddenRowIndexToPositionMap.put(rowIndex, rowPosition);\r
+ }\r
+ }\r
+ }\r
+}\r