]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeLayer.java
Sync git svn branch with SVN repository r33144.
[simantics/platform.git] / bundles / org.simantics.browsing.ui.nattable / src / org / simantics / browsing / ui / nattable / GETreeLayer.java
diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeLayer.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/GETreeLayer.java
new file mode 100644 (file)
index 0000000..dbc3005
--- /dev/null
@@ -0,0 +1,345 @@
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+\r
+import org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;\r
+import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent;\r
+import org.simantics.browsing.ui.nattable.override.TreeLayer2;\r
+import org.simantics.databoard.util.IdentityHashSet;\r
+\r
+import it.unimi.dsi.fastutil.ints.IntRBTreeSet;\r
+/**\r
+ * NatTable TreeLayer for IEcoReportTask tree.\r
+ * \r
+ * Keeps track of collapsed nodes so that current sorting mechanism works.\r
+ * \r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ */\r
+public class GETreeLayer  extends TreeLayer2 {\r
+\r
+       //Set<IEcoReportTask> collapsed = new HashSet<IEcoReportTask>();\r
+       Set<TreeNode> collapsed = new IdentityHashSet<TreeNode>();\r
+       GETreeData treeData;\r
+       Comparator<int[]> comparator = new FirstElementComparator();\r
+       \r
+       public GETreeLayer(IUniqueIndexLayer underlyingLayer, GETreeRowModel<TreeNode> treeRowModel,boolean useDefaultConfiguration) {\r
+               super(underlyingLayer, treeRowModel, useDefaultConfiguration);\r
+               \r
+               if (underlyingLayer instanceof AbstractRowHideShowLayer) {\r
+                       throw new IllegalArgumentException("Cannot use treelayer above row hide layer");\r
+               }\r
+               \r
+               this.treeData = (GETreeData)treeRowModel.getTreeData();\r
+               hiddenPos = new ArrayList<int[]>();\r
+               hiddenPos.add(new int[]{0,0});\r
+       }\r
+\r
+       \r
+       @Override\r
+       public void collapseTreeRow(int parentIndex) {\r
+               TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+               collapsed.add(task);\r
+               task.setExpanded(false);\r
+               super.collapseTreeRow(parentIndex);\r
+       }\r
+       \r
+       public void fullCollapseTreeRow(int parentIndex) {\r
+               TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+               List<Integer> indices = new ArrayList<Integer>();\r
+               \r
+               Stack<TreeNode> stack = new Stack<TreeNode>();\r
+               stack.add(task);\r
+               while (!stack.isEmpty()) {\r
+                       TreeNode t = stack.pop();\r
+                       indices.add(treeData.indexOf(t));\r
+                       stack.addAll(t.getChildren());\r
+               }\r
+               collapseTreeRow(indices);\r
+       }\r
+       \r
+       @Override\r
+       public void expandTreeRow(int parentIndex) {\r
+               TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+               collapsed.remove(task);\r
+               task.setExpanded(true);\r
+               super.expandTreeRow(parentIndex);\r
+       }\r
+       \r
+       public void expandToTreeRow(int parentIndex) {\r
+               TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+               List<TreeNode> ancestors = new ArrayList<TreeNode>();\r
+               while (true) {\r
+                       task = task.getParent();\r
+                       if (task == null)\r
+                               break;\r
+                       else\r
+                               ancestors.add(0, task);\r
+               }\r
+               for (TreeNode t : ancestors) {\r
+                       if (treeData.getDepthOfData(t) >= 0)\r
+                               expandTreeRow(treeData.indexOf(t));\r
+               }\r
+       }\r
+       \r
+       public void fullExpandTreeRow(int parentIndex) {\r
+               TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+               List<Integer> indices = new ArrayList<Integer>();\r
+               \r
+               Stack<TreeNode> stack = new Stack<TreeNode>();\r
+               stack.add(task);\r
+               while (!stack.isEmpty()) {\r
+                       TreeNode t = stack.pop();\r
+                       indices.add(treeData.indexOf(t));\r
+                       stack.addAll(t.getChildren());\r
+               }\r
+               expandTreeRow(indices);\r
+       }\r
+       \r
+       public void collapseTreeRow(int parentIndices[]) {\r
+               List<Integer> rowPositions = new ArrayList<Integer>();\r
+               List<Integer> rowIndexes = new ArrayList<Integer>();\r
+               // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set.\r
+               for (int parentIndex : parentIndices) {\r
+                       if (parentIndex >= 0) {\r
+                               TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+                               if (task != null) {\r
+                                       task.setExpanded(false);\r
+                                       collapsed.add(task);\r
+                               }\r
+                               rowIndexes.addAll(getModel().collapse(parentIndex));\r
+                       }\r
+               }\r
+               for (Integer rowIndex : rowIndexes) {\r
+                       int rowPos = getRowPositionByIndex(rowIndex);\r
+                       //if the rowPos is negative, it is not visible because of hidden state in an underlying layer\r
+                       if (rowPos >= 0) {\r
+                               rowPositions.add(rowPos);\r
+                       }\r
+               }\r
+               //this.getHiddenRowIndexes().addAll(rowIndexes);\r
+               for (int i = 0; i < rowIndexes.size(); i++) {\r
+                       this.getHiddenRowIndexes().add(rowIndexes.get(i));\r
+               }\r
+               invalidateCache();\r
+               fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));\r
+       }\r
+       \r
+       public void collapseTreeRow(List<Integer> parentIndices) {\r
+               List<Integer> rowPositions = new ArrayList<Integer>();\r
+               List<Integer> rowIndexes = new ArrayList<Integer>();\r
+               // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set.\r
+               for (int parentIndex : parentIndices) {\r
+                       if (parentIndex >= 0) {\r
+                               TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+                               task.setExpanded(false);\r
+                               collapsed.add(task);\r
+                               rowIndexes.addAll(getModel().collapse(parentIndex));\r
+                       }\r
+               }\r
+               for (Integer rowIndex : rowIndexes) {\r
+                       int rowPos = getRowPositionByIndex(rowIndex);\r
+                       //if the rowPos is negative, it is not visible because of hidden state in an underlying layer\r
+                       if (rowPos >= 0) {\r
+                               rowPositions.add(rowPos);\r
+                       }\r
+               }\r
+               //this.getHiddenRowIndexes().addAll(rowIndexes);\r
+               for (int i = 0; i < rowIndexes.size(); i++) {\r
+                       this.getHiddenRowIndexes().add(rowIndexes.get(i));\r
+               }\r
+               invalidateCache();\r
+               fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));\r
+       }\r
+       \r
+       public void collapseAllRows() {\r
+               int count = treeData.getElementCount();\r
+               List <Integer> rowIndexes = new ArrayList<Integer>(count);\r
+               for (int i = 0; i < count; i++) {\r
+                       TreeNode t = treeData.getDataAtIndex(i);\r
+                       // we don't want to hide the roots of the tree\r
+                       if (!treeData.isRoot(t)) { \r
+                               rowIndexes.add(i);\r
+                               \r
+                       } \r
+                       t.setExpanded(false);\r
+                       collapsed.add(t);\r
+                       getModel().collapse(i);\r
+                       \r
+               }\r
+               this.getHiddenRowIndexes().addAll(rowIndexes);\r
+               invalidateCache();\r
+               fireLayerEvent(new HideRowPositionsEvent(this, rowIndexes));\r
+       }\r
+       \r
+       public void expandTreeRow(int parentIndices[]) {\r
+               List<Integer> rowIndexes = new ArrayList<Integer>();\r
+               for (int parentIndex : parentIndices) {\r
+                       TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+                       task.setExpanded(true);\r
+                       collapsed.remove(task);\r
+                       rowIndexes.addAll(getModel().expand(parentIndex));\r
+               }\r
+               \r
+               //Implementation uses tree set, so removing in reverse order is faster.\r
+               for (int i = rowIndexes.size() -1 ; i >= 0; i--) {\r
+                       this.getHiddenRowIndexes().remove(rowIndexes.get(i));\r
+               }\r
+               //this.getHiddenRowIndexes().removeAll(rowIndexes);\r
+               invalidateCache();\r
+               fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+       }\r
+       \r
+       public void expandTreeRow(List<Integer> parentIndices) {\r
+               List<Integer> rowIndexes = new ArrayList<Integer>();\r
+               for (int parentIndex : parentIndices) {\r
+                       TreeNode task = treeData.getDataAtIndex(parentIndex);\r
+                       task.setExpanded(true);\r
+                       collapsed.remove(task);\r
+                       rowIndexes.addAll(getModel().expand(parentIndex));\r
+               }\r
+               \r
+               //Implementation uses tree set, so removing in reverse order is faster.\r
+               for (int i = rowIndexes.size() -1 ; i >= 0; i--) {\r
+                       this.getHiddenRowIndexes().remove(rowIndexes.get(i));\r
+               }\r
+               //this.getHiddenRowIndexes().removeAll(rowIndexes);\r
+               invalidateCache();\r
+               fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+       }\r
+       \r
+       public void expandAllRows() {\r
+               Collection<Integer> parentIndices = getHiddenRowIndexes();\r
+               List<Integer> rowIndexes = new ArrayList<Integer>();\r
+               for (int parentIndex : parentIndices) {\r
+                       rowIndexes.addAll(getModel().expand(parentIndex));\r
+               }\r
+               for (TreeNode t : collapsed)\r
+                       t.setExpanded(true);\r
+               collapsed.clear();\r
+               getHiddenRowIndexes().clear();\r
+               ((GETreeRowModel)getModel()).clear();\r
+               invalidateCache();\r
+               fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));\r
+       }\r
+       \r
+       @Override\r
+       protected void invalidateCache() {\r
+               super.invalidateCache();\r
+               hiddenPos.clear();\r
+               hiddenPos.add(new int[]{0,0});\r
+       }\r
+       \r
+       @Override\r
+       public void handleLayerEvent(ILayerEvent event) {\r
+               // Currently sorting is implemented by sorting the underlaying list.\r
+               // Since all layers are storing just indices, we have to keep track the indices after sorting,\r
+               // and refresh the layers accordingly.\r
+               \r
+               // Another option would use some sort of sorting layers, so that the original data is kept intact, and\r
+               // sorting layer would map the row indexes to sorted row positions.\r
+               \r
+               // preserve collapsed nodes \r
+               Set<TreeNode> coll = null;\r
+               if (event instanceof IStructuralChangeEvent) {\r
+                       IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event;\r
+                       if (structuralChangeEvent.isVerticalStructureChanged()) {\r
+                               // expand old indices\r
+                               ((GETreeRowModel)getModel()).clear();\r
+                               getHiddenRowIndexes().clear();\r
+                               coll = collapsed;\r
+                       }\r
+               }\r
+               super.handleLayerEvent(event);\r
+               if (coll != null) {\r
+                       // collapse new indices\r
+                       int ind[] = new int[coll.size()];\r
+                       Iterator<TreeNode> iter = coll.iterator();\r
+                       for (int i = 0; i < ind.length; i++) {\r
+                               ind[i] = treeData.indexOf(iter.next());\r
+                       }\r
+                       collapseTreeRow(ind);\r
+               }\r
+       }\r
+       \r
+       public Set<TreeNode> getCollapsed() {\r
+               return collapsed;\r
+       }\r
+       \r
+       List<int[]> hiddenPos;\r
+       \r
+       @Override\r
+       public int getStartYOfRowPosition(int localRowPosition) {\r
+               Integer cachedStartY = startYCache.get(Integer.valueOf(localRowPosition));\r
+               if (cachedStartY != null) {\r
+                       return cachedStartY.intValue();\r
+               }\r
+               \r
+               IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();\r
+               int underlyingPosition = localToUnderlyingRowPosition(localRowPosition);\r
+               int underlyingStartY = underlyingLayer.getStartYOfRowPosition(underlyingPosition);\r
+               if (underlyingStartY < 0) {\r
+                       return -1;\r
+               }\r
+\r
+               int h = 0;\r
+               int start = 0;\r
+               \r
+               if (hiddenPos.size() < 2) {                       // is cache empty? (hiddenPos contains {0,0} element by default) \r
+                       if (getHiddenRowIndexes().size() > 0)         // check if there are hidden rows.\r
+                               start = getHiddenRowIndexes().iterator().next();\r
+               } else {\r
+                       int[] d =  hiddenPos.get(hiddenPos.size()-1); // take the last element of the cache.\r
+                       start = d[0]+1;                               // set to search from the next element.\r
+                       h = d[1];\r
+               }\r
+               if (start < underlyingPosition) {                 // check if we can find the amount of hidden space from the cache.\r
+                                                                     // cache positions of hidden nodes and hidden space.\r
+                       //for (Integer hiddenIndex : ((TreeSet<Integer>)getHiddenRowIndexes()).tailSet(start)) {\r
+                       for (int hiddenIndex : ((IntRBTreeSet)getHiddenRowIndexes()).tailSet(start)) {\r
+                                                                         // safety check (could be disabled, but this does not seem to cause considerable performance hit)\r
+                               int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex);//.intValue());\r
+                               if (hiddenPosition != hiddenIndex)//.intValue())\r
+                                       throw new RuntimeException("Underlying layer is swithing indices");\r
+                               if (hiddenPosition >= 0 && hiddenPosition <= underlyingPosition) {\r
+                                       h += underlyingLayer.getRowHeightByPosition(hiddenPosition); \r
+                                       hiddenPos.add(new int[]{hiddenPosition,h});\r
+                               } else if (hiddenPosition > underlyingPosition) {\r
+                                       break;\r
+                               }\r
+                       }\r
+               } else {\r
+                       // use binary search to find hidden space.\r
+                       h = 0;\r
+                       int index = Collections.binarySearch(hiddenPos, new int[]{underlyingPosition,0}, comparator);\r
+                       if (index < 0) { // exact element is not cached, but we can use the closest match.\r
+                               index = -index-2;\r
+                       }  \r
+                       h = hiddenPos.get(index)[1];\r
+               }\r
+               underlyingStartY -= h;\r
+               startYCache.put(Integer.valueOf(localRowPosition), Integer.valueOf(underlyingStartY));\r
+               return underlyingStartY;\r
+       }\r
+       \r
+       \r
+       private static class FirstElementComparator implements Comparator<int[]> {\r
+               @Override\r
+               public int compare(int[] o1, int[] o2) {\r
+                       return o1[0]-o2[0];\r
+               }\r
+       }\r
+       \r
+}
\ No newline at end of file