X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.browsing.ui.nattable%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fnattable%2FGETreeLayer.java;h=6adefd90630fe605e52127b42030459eef618e37;hp=309e4daa85f5bfa084416b043449d90be8250b65;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 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 index 309e4daa8..6adefd906 100644 --- 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 @@ -1,400 +1,400 @@ -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 - * - */ -public class GETreeLayer extends TreeLayer2 { - - //Set collapsed = new HashSet(); - Set expanded = new IdentityHashSet(); - GETreeData treeData; - Comparator comparator = new FirstElementComparator(); - - public GETreeLayer(IUniqueIndexLayer underlyingLayer, GETreeRowModel 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(); - 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 indices = new ArrayList(); - - Stack stack = new Stack(); - 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 ancestors = new ArrayList(); - 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 indices = new ArrayList(); - - Stack stack = new Stack(); - 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 rowPositions = new ArrayList(); - List rowIndexes = new ArrayList(); - // 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 parentIndices) { - List rowPositions = new ArrayList(); - List rowIndexes = new ArrayList(); - // 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 rowIndexes = new ArrayList(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 rowIndexes = new ArrayList(); - 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 parentIndices) { - List rowIndexes = new ArrayList(); - 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 parentIndices = getHiddenRowIndexes(); -// List rowIndexes = new ArrayList(); -// 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 rowIndexes = new ArrayList(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 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 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 getExpanded() { - return expanded; - } - - List 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)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 { - @Override - public int compare(int[] o1, int[] o2) { - return o1[0]-o2[0]; - } - } - +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 + * + */ +public class GETreeLayer extends TreeLayer2 { + + //Set collapsed = new HashSet(); + Set expanded = new IdentityHashSet(); + GETreeData treeData; + Comparator comparator = new FirstElementComparator(); + + public GETreeLayer(IUniqueIndexLayer underlyingLayer, GETreeRowModel 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(); + 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 indices = new ArrayList(); + + Stack stack = new Stack(); + 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 ancestors = new ArrayList(); + 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 indices = new ArrayList(); + + Stack stack = new Stack(); + 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 rowPositions = new ArrayList(); + List rowIndexes = new ArrayList(); + // 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 parentIndices) { + List rowPositions = new ArrayList(); + List rowIndexes = new ArrayList(); + // 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 rowIndexes = new ArrayList(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 rowIndexes = new ArrayList(); + 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 parentIndices) { + List rowIndexes = new ArrayList(); + 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 parentIndices = getHiddenRowIndexes(); +// List rowIndexes = new ArrayList(); +// 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 rowIndexes = new ArrayList(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 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 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 getExpanded() { + return expanded; + } + + List 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)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 { + @Override + public int compare(int[] o1, int[] o2) { + return o1[0]-o2[0]; + } + } + } \ No newline at end of file