]> gerrit.simantics Code Review - simantics/platform.git/blob - 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
1 package org.simantics.browsing.ui.nattable;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.HashSet;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Set;
10 import java.util.Stack;
11
12 import org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer;
13 import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent;
14 import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;
15 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
16 import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
17 import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent;
18 import org.simantics.browsing.ui.nattable.override.TreeLayer2;
19 import org.simantics.databoard.util.IdentityHashSet;
20
21 import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
22 /**
23  * NatTable TreeLayer for IEcoReportTask tree.
24  * 
25  * Keeps track of collapsed nodes so that current sorting mechanism works.
26  * 
27  * 
28  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
29  *
30  */
31 public class GETreeLayer  extends TreeLayer2 {
32
33         //Set<IEcoReportTask> collapsed = new HashSet<IEcoReportTask>();
34         Set<TreeNode> expanded = new IdentityHashSet<TreeNode>();
35         GETreeData treeData;
36         Comparator<int[]> comparator = new FirstElementComparator();
37         
38         public GETreeLayer(IUniqueIndexLayer underlyingLayer, GETreeRowModel<TreeNode> treeRowModel,boolean useDefaultConfiguration) {
39                 super(underlyingLayer, treeRowModel, useDefaultConfiguration);
40                 
41                 if (underlyingLayer instanceof AbstractRowHideShowLayer) {
42                         throw new IllegalArgumentException("Cannot use treelayer above row hide layer");
43                 }
44                 
45                 this.treeData = (GETreeData)treeRowModel.getTreeData();
46                 hiddenPos = new ArrayList<int[]>();
47                 hiddenPos.add(new int[]{0,0});
48         }
49
50         
51         @Override
52         public void collapseTreeRow(int parentIndex) {
53                 TreeNode task = treeData.getDataAtIndex(parentIndex);
54                 expanded.remove(task);
55                 task.setExpanded(false);
56                 super.collapseTreeRow(parentIndex);
57         }
58         
59         public void fullCollapseTreeRow(int parentIndex) {
60                 TreeNode task = treeData.getDataAtIndex(parentIndex);
61                 List<Integer> indices = new ArrayList<Integer>();
62                 
63                 Stack<TreeNode> stack = new Stack<TreeNode>();
64                 stack.add(task);
65                 while (!stack.isEmpty()) {
66                         TreeNode t = stack.pop();
67                         indices.add(treeData.indexOf(t));
68                         stack.addAll(t.getChildren());
69                 }
70                 collapseTreeRow(indices);
71         }
72         
73         @Override
74         public void expandTreeRow(int parentIndex) {
75                 TreeNode task = treeData.getDataAtIndex(parentIndex);
76                 expanded.add(task);
77                 task.setExpanded(true);
78                 super.expandTreeRow(parentIndex);
79         }
80         
81         public void expandToTreeRow(int parentIndex) {
82                 TreeNode task = treeData.getDataAtIndex(parentIndex);
83                 List<TreeNode> ancestors = new ArrayList<TreeNode>();
84                 while (true) {
85                         task = task.getParent();
86                         if (task == null)
87                                 break;
88                         else
89                                 ancestors.add(0, task);
90                 }
91                 for (TreeNode t : ancestors) {
92                         if (treeData.getDepthOfData(t) >= 0)
93                                 expandTreeRow(treeData.indexOf(t));
94                 }
95         }
96         
97         public void fullExpandTreeRow(int parentIndex) {
98                 TreeNode task = treeData.getDataAtIndex(parentIndex);
99                 List<Integer> indices = new ArrayList<Integer>();
100                 
101                 Stack<TreeNode> stack = new Stack<TreeNode>();
102                 stack.add(task);
103                 while (!stack.isEmpty()) {
104                         TreeNode t = stack.pop();
105                         indices.add(treeData.indexOf(t));
106                         stack.addAll(t.getChildren());
107                 }
108                 expandTreeRow(indices);
109         }
110         
111         public void collapseTreeRow(int parentIndices[]) {
112                 List<Integer> rowPositions = new ArrayList<Integer>();
113                 List<Integer> rowIndexes = new ArrayList<Integer>();
114                 // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set.
115                 for (int parentIndex : parentIndices) {
116                         if (parentIndex >= 0) {
117                                 TreeNode task = treeData.getDataAtIndex(parentIndex);
118                                 if (task != null) {
119                                         task.setExpanded(false);
120                                         expanded.remove(task);
121                                 }
122                                 rowIndexes.addAll(getModel().collapse(parentIndex));
123                         }
124                 }
125                 for (Integer rowIndex : rowIndexes) {
126                         int rowPos = getRowPositionByIndex(rowIndex);
127                         //if the rowPos is negative, it is not visible because of hidden state in an underlying layer
128                         if (rowPos >= 0) {
129                                 rowPositions.add(rowPos);
130                         }
131                 }
132                 //this.getHiddenRowIndexes().addAll(rowIndexes);
133                 for (int i = 0; i < rowIndexes.size(); i++) {
134                         this.getHiddenRowIndexes().add(rowIndexes.get(i));
135                 }
136                 invalidateCache();
137                 fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));
138         }
139         
140         public void collapseTreeRow(List<Integer> parentIndices) {
141                 List<Integer> rowPositions = new ArrayList<Integer>();
142                 List<Integer> rowIndexes = new ArrayList<Integer>();
143                 // while this approach may collect some of the row indices several times, it is faster than up-keeping hash set.
144                 for (int parentIndex : parentIndices) {
145                         if (parentIndex >= 0) {
146                                 TreeNode task = treeData.getDataAtIndex(parentIndex);
147                                 task.setExpanded(false);
148                                 expanded.remove(task);
149                                 rowIndexes.addAll(getModel().collapse(parentIndex));
150                         }
151                 }
152                 for (Integer rowIndex : rowIndexes) {
153                         int rowPos = getRowPositionByIndex(rowIndex);
154                         //if the rowPos is negative, it is not visible because of hidden state in an underlying layer
155                         if (rowPos >= 0) {
156                                 rowPositions.add(rowPos);
157                         }
158                 }
159                 //this.getHiddenRowIndexes().addAll(rowIndexes);
160                 for (int i = 0; i < rowIndexes.size(); i++) {
161                         this.getHiddenRowIndexes().add(rowIndexes.get(i));
162                 }
163                 invalidateCache();
164                 fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));
165         }
166         
167         public void collapseAllRows() {
168                 int count = treeData.getElementCount();
169                 List <Integer> rowIndexes = new ArrayList<Integer>(count);
170                 for (int i = 0; i < count; i++) {
171                         TreeNode t = treeData.getDataAtIndex(i);
172                         // we don't want to hide the roots of the tree
173                         if (!treeData.isRoot(t)) { 
174                                 rowIndexes.add(i);
175                                 
176                         } 
177                         t.setExpanded(false);
178                         expanded.remove(t);
179                         getModel().collapse(i);
180                         
181                 }
182                 this.getHiddenRowIndexes().addAll(rowIndexes);
183                 invalidateCache();
184                 fireLayerEvent(new HideRowPositionsEvent(this, rowIndexes));
185         }
186         
187         public void expandTreeRow(int parentIndices[]) {
188                 List<Integer> rowIndexes = new ArrayList<Integer>();
189                 for (int parentIndex : parentIndices) {
190                         TreeNode task = treeData.getDataAtIndex(parentIndex);
191                         task.setExpanded(true);
192                         expanded.add(task);
193                         rowIndexes.addAll(getModel().expand(parentIndex));
194                         rowIndexes.add(parentIndex);
195                 }
196                 
197                 //Implementation uses tree set, so removing in reverse order is faster.
198                 for (int i = rowIndexes.size() -1 ; i >= 0; i--) {
199                         this.getHiddenRowIndexes().remove(rowIndexes.get(i));
200                 }
201                 //this.getHiddenRowIndexes().removeAll(rowIndexes);
202                 invalidateCache();
203                 fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));
204         }
205         
206         public void expandTreeRow(List<Integer> parentIndices) {
207                 List<Integer> rowIndexes = new ArrayList<Integer>();
208                 for (int parentIndex : parentIndices) {
209                         TreeNode task = treeData.getDataAtIndex(parentIndex);
210                         task.setExpanded(true);
211                         expanded.add(task);
212                         rowIndexes.addAll(getModel().expand(parentIndex));
213                 }
214                 
215                 //Implementation uses tree set, so removing in reverse order is faster.
216                 for (int i = rowIndexes.size() -1 ; i >= 0; i--) {
217                         this.getHiddenRowIndexes().remove(rowIndexes.get(i));
218                 }
219                 //this.getHiddenRowIndexes().removeAll(rowIndexes);
220                 invalidateCache();
221                 fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));
222         }
223         
224 //      public void expandAllRows() {
225 //              Collection<Integer> parentIndices = getHiddenRowIndexes();
226 //              List<Integer> rowIndexes = new ArrayList<Integer>();
227 //              for (int parentIndex : parentIndices) {
228 //                      rowIndexes.addAll(getModel().expand(parentIndex));
229 //              }
230 //              for (TreeNode t : collapsed)
231 //                      t.setExpanded(true);
232 //              collapsed.clear();
233 //              getHiddenRowIndexes().clear();
234 //              ((GETreeRowModel)getModel()).clear();
235 //              invalidateCache();
236 //              fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));
237 //      }
238         
239         @Override
240         protected void invalidateCache() {
241                 super.invalidateCache();
242                 hiddenPos.clear();
243                 hiddenPos.add(new int[]{0,0});
244         }
245         
246         private void _collapseAllRows() {
247                 int count = treeData.getElementCount();
248                 List <Integer> rowIndexes = new ArrayList<Integer>(count);
249                 for (int i = 0; i < count; i++) {
250                         TreeNode t = treeData.getDataAtIndex(i);
251                         // we don't want to hide the roots of the tree
252                         if (!treeData.isRoot(t)) { 
253                                 rowIndexes.add(i);
254                                 
255                         } 
256                         getModel().collapse(i);
257                         
258                 }
259                 this.getHiddenRowIndexes().addAll(rowIndexes);
260                 invalidateCache();
261         }
262         
263         @Override
264         public void handleLayerEvent(ILayerEvent event) {
265                 // Currently sorting is implemented by sorting the underlaying list.
266                 // Since all layers are storing just indices, we have to keep track the indices after sorting,
267                 // and refresh the layers accordingly.
268                 
269                 // Another option would use some sort of sorting layers, so that the original data is kept intact, and
270                 // sorting layer would map the row indexes to sorted row positions.
271                 
272                 // preserve expanded nodes 
273                 Set<TreeNode> coll = null;
274 //              int hiddenCount = 0;
275                 if (event instanceof IStructuralChangeEvent) {
276                         IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event;
277                         if (structuralChangeEvent.isVerticalStructureChanged()) {
278                                 // store old indices
279                                 internalRefresh = true;
280                                 ((GETreeRowModel<?>)getModel()).clear();
281 //                              hiddenCount = getHiddenRowIndexes().size();
282                                 getHiddenRowIndexes().clear();
283                                 // store expanded nodes and clear disposed nodes.
284                                 coll = new HashSet<>();
285                                 for (TreeNode n : expanded)
286                                         if (!n.isDisposed())
287                                                 coll.add(n);
288                                 expanded.clear();
289                                 expanded.addAll(coll);
290                                 // filter hidden nodes (nodes that have collapsed ancestors)
291                                 coll.clear();
292                                 for (TreeNode n : expanded)
293                                         if (!n.isHidden())
294                                                 coll.add(n);
295                         }
296                 }
297                 super.handleLayerEvent(event);
298                 if (coll != null) {
299                         _collapseAllRows();
300                         // expand new indices
301                         int ind[] = new int[coll.size()];
302                         Iterator<TreeNode> iter = coll.iterator();
303                         for (int i = 0; i < ind.length; i++) {
304                                 ind[i] = treeData.indexOf(iter.next());
305                         }
306                         expandTreeRow(ind);
307 //                      if (getHiddenRowIndexes().size() != hiddenCount) {
308 //                              System.out.println(getHiddenRowIndexes().size() + " != " + hiddenCount);
309 //                              ((GETreeRowModel<?>)getModel()).clear();
310 //                              getHiddenRowIndexes().clear();
311 //                              _collapseAllRows();
312 //                              //collapseAll();
313 //                              // expand new indices
314 //                              iter = coll.iterator();
315 //                              for (int i = 0; i < ind.length; i++) {
316 //                                      ind[i] = treeData.indexOf(iter.next());
317 //                              }
318 //                              expandTreeRow(ind);
319 //                      }
320                         internalRefresh = false;
321                 }
322         }
323         
324         private boolean internalRefresh = false;
325         
326         public void fireLayerEvent(ILayerEvent event) {
327                 if (!internalRefresh)
328                         super.fireLayerEvent(event);
329                 
330         }
331         
332         public Set<TreeNode> getExpanded() {
333                 return expanded;
334         }
335         
336         List<int[]> hiddenPos;
337         
338         @Override
339         public int getStartYOfRowPosition(int localRowPosition) {
340                 Integer cachedStartY = startYCache.get(Integer.valueOf(localRowPosition));
341                 if (cachedStartY != null) {
342                         return cachedStartY.intValue();
343                 }
344                 
345                 IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();
346                 int underlyingPosition = localToUnderlyingRowPosition(localRowPosition);
347                 int underlyingStartY = underlyingLayer.getStartYOfRowPosition(underlyingPosition);
348                 if (underlyingStartY < 0) {
349                         return -1;
350                 }
351
352                 int h = 0;
353                 int start = 0;
354                 
355                 if (hiddenPos.size() < 2) {                       // is cache empty? (hiddenPos contains {0,0} element by default) 
356                         if (getHiddenRowIndexes().size() > 0)         // check if there are hidden rows.
357                                 start = getHiddenRowIndexes().iterator().next();
358                 } else {
359                         int[] d =  hiddenPos.get(hiddenPos.size()-1); // take the last element of the cache.
360                         start = d[0]+1;                               // set to search from the next element.
361                         h = d[1];
362                 }
363                 if (start < underlyingPosition) {                 // check if we can find the amount of hidden space from the cache.
364                                                                       // cache positions of hidden nodes and hidden space.
365                         //for (Integer hiddenIndex : ((TreeSet<Integer>)getHiddenRowIndexes()).tailSet(start)) {
366                         for (int hiddenIndex : ((IntRBTreeSet)getHiddenRowIndexes()).tailSet(start)) {
367                                                                           // safety check (could be disabled, but this does not seem to cause considerable performance hit)
368                                 int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex);//.intValue());
369                                 if (hiddenPosition != hiddenIndex)//.intValue())
370                                         throw new RuntimeException("Underlying layer is swithing indices");
371                                 if (hiddenPosition >= 0 && hiddenPosition <= underlyingPosition) {
372                                         h += underlyingLayer.getRowHeightByPosition(hiddenPosition); 
373                                         hiddenPos.add(new int[]{hiddenPosition,h});
374                                 } else if (hiddenPosition > underlyingPosition) {
375                                         break;
376                                 }
377                         }
378                 } else {
379                         // use binary search to find hidden space.
380                         h = 0;
381                         int index = Collections.binarySearch(hiddenPos, new int[]{underlyingPosition,0}, comparator);
382                         if (index < 0) { // exact element is not cached, but we can use the closest match.
383                                 index = -index-2;
384                         }  
385                         h = hiddenPos.get(index)[1];
386                 }
387                 underlyingStartY -= h;
388                 startYCache.put(Integer.valueOf(localRowPosition), Integer.valueOf(underlyingStartY));
389                 return underlyingStartY;
390         }
391         
392         
393         private static class FirstElementComparator implements Comparator<int[]> {
394                 @Override
395                 public int compare(int[] o1, int[] o2) {
396                         return o1[0]-o2[0];
397                 }
398         }
399         
400 }