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