1:
37:
38: package ;
39:
40: import ;
41:
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50:
51: import ;
52:
53:
63: public class VariableHeightLayoutCache
64: extends AbstractLayoutCache
65: {
66:
67: private static final Rectangle RECT_CACHE = new Rectangle();
68:
69:
72: class NodeRecord
73: {
74: NodeRecord(int aRow, int aDepth, Object aNode, Object aParent)
75: {
76: row = aRow;
77: depth = aDepth;
78: parent = aParent;
79: node = aNode;
80: isExpanded = expanded.contains(aNode);
81: bounds = new Rectangle(0, -1, 0, 0);
82: }
83:
84:
87: final int row;
88:
89:
92: final int depth;
93:
94:
97: final Object parent;
98:
99:
102: final Object node;
103:
104:
108: final boolean isExpanded;
109:
110:
113: Rectangle bounds;
114:
115:
119: private TreePath path;
120:
121:
125: TreePath getPath()
126: {
127: if (path == null)
128: {
129: boolean lastChild = false;
130: if (parent != null)
131: {
132: int nc = treeModel.getChildCount(parent);
133: if (nc > 0)
134: {
135: int n = treeModel.getIndexOfChild(parent, node);
136: if (n == nc - 1)
137: lastChild = true;
138: }
139: }
140:
141: LinkedList<Object> lpath = new LinkedList<Object>();
142: NodeRecord rp = this;
143: while (rp != null)
144: {
145: lpath.addFirst(rp.node);
146: if (rp.parent != null)
147: {
148: Object parent = rp.parent;
149: rp = nodes.get(parent);
150:
151: if (rp == null)
152: lpath.addFirst(parent);
153: }
154: else
155: rp = null;
156: }
157: path = new GnuPath(lpath.toArray(), lastChild);
158: }
159: return path;
160: }
161:
162:
165: Rectangle getBounds()
166: {
167: return bounds;
168: }
169: }
170:
171:
174: Set<Object> expanded = new HashSet<Object>();
175:
176:
179: Hashtable<Object,NodeRecord> nodes = new Hashtable<Object,NodeRecord>();
180:
181:
184: ArrayList<Object> row2node = new ArrayList<Object>();
185:
186:
189: boolean dirty;
190:
191:
194: int totalHeight;
195:
196:
199: int maximalWidth;
200:
201:
206: public VariableHeightLayoutCache()
207: {
208:
209: }
210:
211:
218: public int getRowCount()
219: {
220: if (dirty) update();
221: return row2node.size();
222: }
223:
224:
227: private final void update()
228: {
229: nodes.clear();
230: row2node.clear();
231:
232: totalHeight = maximalWidth = 0;
233:
234: if (treeModel == null)
235: return;
236:
237: Object root = treeModel.getRoot();
238: countRows(root, null, 0, 0);
239: dirty = false;
240: }
241:
242:
245: private final int countRows(Object node, Object parent, int depth, int y)
246: {
247: boolean visible = node != treeModel.getRoot() || rootVisible;
248: int row = row2node.size();
249: if (visible)
250: {
251: row2node.add(node);
252: }
253: NodeRecord nr = new NodeRecord(row, depth, node, parent);
254: NodeDimensions d = getNodeDimensions();
255: Rectangle r = RECT_CACHE;
256: if (d != null)
257: r = d.getNodeDimensions(node, row, depth, nr.isExpanded, r);
258: else
259: r.setBounds(0, 0, 0, 0);
260:
261: if (! visible)
262: r.y = -1;
263: else
264: r.y = Math.max(0, y);
265:
266: if (isFixedRowHeight())
267: r.height = getRowHeight();
268:
269: nr.bounds.setBounds(r);
270: nodes.put(node, nr);
271:
272: if (visible)
273: y += r.height;
274:
275: int sc = treeModel.getChildCount(node);
276: int deeper = depth + 1;
277: if (expanded.contains(node))
278: {
279: for (int i = 0; i < sc; i++)
280: {
281: Object child = treeModel.getChild(node, i);
282: y = countRows(child, node, deeper, y);
283: }
284: }
285: return y;
286: }
287:
288:
293: public void invalidatePathBounds(TreePath path)
294: {
295: NodeRecord r = nodes.get(path.getLastPathComponent());
296: if (r != null)
297: r.bounds = null;
298: }
299:
300:
303: public void invalidateSizes()
304: {
305: dirty = true;
306: }
307:
308:
317: public void setExpandedState(TreePath path, boolean isExpanded)
318: {
319: if (isExpanded)
320: {
321: int length = path.getPathCount();
322: for (int i = 0; i < length; i++)
323: expanded.add(path.getPathComponent(i));
324: }
325: else
326: expanded.remove(path.getLastPathComponent());
327:
328: dirty = true;
329: }
330:
331:
336: public boolean isExpanded(TreePath path)
337: {
338: return expanded.contains(path.getLastPathComponent());
339: }
340:
341:
348: public Rectangle getBounds(TreePath path, Rectangle rect)
349: {
350: if (path == null)
351: return null;
352: if (dirty)
353: update();
354:
355: Object last = path.getLastPathComponent();
356: Rectangle result = null;
357: NodeRecord r = nodes.get(last);
358: if (r != null)
359: {
360:
361:
362: result = rect;
363: if (result == null)
364: result = new Rectangle(r.bounds);
365: else
366: result.setBounds(r.bounds);
367: }
368: return result;
369: }
370:
371:
377: public TreePath getPathForRow(int row)
378: {
379: if (dirty)
380: update();
381:
382: TreePath path = null;
383:
384: Enumeration<NodeRecord> nodesEnum = nodes.elements();
385: while (nodesEnum.hasMoreElements() && path == null)
386: {
387: NodeRecord record = nodesEnum.nextElement();
388: if (record.row == row)
389: path = record.getPath();
390: }
391: return path;
392: }
393:
394:
400: public int getRowForPath(TreePath path)
401: {
402: if (path == null)
403: return -1;
404:
405: if (dirty)
406: update();
407:
408: NodeRecord r = nodes.get(path.getLastPathComponent());
409: if (r == null)
410: return - 1;
411: else
412: return r.row;
413: }
414:
415:
422: public TreePath getPathClosestTo(int x, int y)
423: {
424: if (dirty)
425: update();
426:
427:
428: NodeRecord best = null;
429: NodeRecord r;
430: Enumeration<NodeRecord> en = nodes.elements();
431:
432: int dist = Integer.MAX_VALUE;
433:
434: while (en.hasMoreElements() && dist > 0)
435: {
436: r = en.nextElement();
437: if (best == null)
438: {
439: best = r;
440: dist = distance(r.getBounds(), x, y);
441: }
442: else
443: {
444: int rr = distance(r.getBounds(), x, y);
445: if (rr < dist)
446: {
447: best = r;
448: dist = rr;
449: }
450: }
451: }
452:
453: if (best == null)
454: return null;
455: else
456: return best.getPath();
457: }
458:
459:
463: int distance(Rectangle r, int x, int y)
464: {
465: if (y < r.y)
466: return r.y - y;
467: else if (y > r.y + r.height - 1)
468: return y - (r.y + r.height - 1);
469: else
470: return 0;
471: }
472:
473:
482: public int getVisibleChildCount(TreePath path)
483: {
484: if (! isExpanded(path) || treeModel == null)
485: return 0;
486: else
487: return treeModel.getChildCount(path.getLastPathComponent());
488: }
489:
490:
497: public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath)
498: {
499: if (dirty)
500: update();
501: Vector<TreePath> p = new Vector<TreePath>(parentPath.getPathCount());
502: Object node;
503: NodeRecord nr;
504:
505: for (int i = 0; i < parentPath.getPathCount(); i++)
506: {
507: node = parentPath.getPathComponent(i);
508: nr = nodes.get(node);
509: if (nr != null && nr.row >= 0)
510: p.add((TreePath) node);
511: }
512: return p.elements();
513: }
514:
515:
523: public boolean getExpandedState(TreePath path)
524: {
525: return expanded.contains(path.getLastPathComponent());
526: }
527:
528:
533: public void treeNodesChanged(TreeModelEvent event)
534: {
535: dirty = true;
536: }
537:
538:
543: public void treeNodesInserted(TreeModelEvent event)
544: {
545: dirty = true;
546: }
547:
548:
553: public void treeNodesRemoved(TreeModelEvent event)
554: {
555: dirty = true;
556: }
557:
558:
563: public void treeStructureChanged(TreeModelEvent event)
564: {
565: dirty = true;
566: }
567:
568:
571: public void setModel(TreeModel newModel)
572: {
573: treeModel = newModel;
574: dirty = true;
575: if (treeModel != null)
576: {
577:
578: expanded.add(treeModel.getRoot());
579: }
580: }
581:
582:
589: public void setRootVisible(boolean visible)
590: {
591: rootVisible = visible;
592: dirty = true;
593: }
594:
595:
598: public int getPreferredHeight()
599: {
600: if (dirty)
601: update();
602: int height = 0;
603: int rowCount = getRowCount();
604: if (rowCount > 0)
605: {
606: NodeRecord last = nodes.get(row2node.get(rowCount - 1));
607: height = last.bounds.y + last.bounds.height;
608: }
609: return height;
610: }
611:
612:
615: public int getPreferredWidth(Rectangle value)
616: {
617: if (dirty)
618: update();
619:
620: maximalWidth = 0;
621: Enumeration<NodeRecord> en = nodes.elements();
622: while (en.hasMoreElements())
623: {
624: NodeRecord nr = en.nextElement();
625: if (nr != null)
626: {
627: Rectangle r = nr.getBounds();
628: int width = r.x + r.width;
629: if (width > maximalWidth)
630: maximalWidth = width;
631: }
632: }
633: return maximalWidth;
634: }
635:
636:
641: public void setNodeDimensions(NodeDimensions dim)
642: {
643: super.setNodeDimensions(dim);
644: dirty = true;
645: }
646:
647:
652: public void setRowHeight(int height)
653: {
654: super.setRowHeight(height);
655: dirty = true;
656: }
657: }