-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