001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Anton Avtamonov
019: * @version $Revision$
020: */package javax.swing.tree;
021:
022: import java.awt.Rectangle;
023: import java.util.Enumeration;
024:
025: import javax.swing.event.TreeModelEvent;
026:
027: public class VariableHeightLayoutCache extends AbstractLayoutCache {
028: public void setRootVisible(final boolean rootVisible) {
029: this .rootVisible = rootVisible;
030: if (getStateRoot() != null) {
031: getStateRoot().invalidateSubtree();
032: }
033: }
034:
035: public void setRowHeight(final int rowHeight) {
036: super .setRowHeight(rowHeight);
037: if (getStateRoot() != null) {
038: getStateRoot().invalidateSubtree();
039: }
040: }
041:
042: public void setNodeDimensions(final NodeDimensions nd) {
043: super .setNodeDimensions(nd);
044: if (getStateRoot() != null) {
045: getStateRoot().invalidateSubtree();
046: }
047: }
048:
049: public void setExpandedState(final TreePath path,
050: final boolean isExpanded) {
051: setExpandedStateImpl(path, isExpanded);
052: }
053:
054: public boolean getExpandedState(final TreePath path) {
055: return getExpandedStateImpl(path);
056: }
057:
058: public Rectangle getBounds(final TreePath path,
059: final Rectangle placeIn) {
060: if (!isRoot(path) && !isVisible(path)) {
061: return null;
062: }
063:
064: if (isFixedRowHeight()) {
065: return getFixedHeightBoundsImpl(path, placeIn);
066: }
067:
068: if (getNodeDimensions() == null) {
069: return new Rectangle();
070: }
071:
072: if (isRoot(path)) {
073: Rectangle result = getNodeDimensions(getStateRoot()
074: .getModelNode(), isRootVisible() ? 0 : -1, 0,
075: getStateRoot().isExpanded(), placeIn);
076: result.y = 0;
077: return result;
078: }
079:
080: VariableHeightStateNode parentNode = (VariableHeightStateNode) getStateNodeForPath(path
081: .getParentPath());
082: StateNode node = parentNode.getChild(path
083: .getLastPathComponent());
084: Rectangle result = getNodeDimensions(path
085: .getLastPathComponent(), getRowForPath(path),
086: getPathDepth(path), node != null && node.isExpanded(),
087: placeIn);
088: result.y = 0;
089:
090: Object currentModelNode = path.getLastPathComponent();
091: while (parentNode != null) {
092: if (!parentNode.isRoot() || isRootVisible()) {
093: result.y += parentNode.getHeight();
094: }
095: if (parentNode.isExpanded()) {
096: int modelIndex = parentNode
097: .getModelIndexOfChild(currentModelNode);
098: for (int i = 0; i < modelIndex; i++) {
099: result.y += parentNode.getChildrenHeights()[i];
100: }
101: }
102: currentModelNode = parentNode.getModelNode();
103: parentNode = (VariableHeightStateNode) parentNode
104: .getParent();
105: }
106:
107: return result;
108: }
109:
110: public TreePath getPathForRow(final int row) {
111: if (row < 0 || row >= getRowCount()) {
112: return null;
113: }
114:
115: if (isRootVisible() && row == 0) {
116: return getStateRoot().getModelPath();
117: }
118:
119: VariableHeightStateNode parent = (VariableHeightStateNode) getStateRoot();
120:
121: while (true) {
122: if (parent.getChildCount() == 0) {
123: int modelIndex = row - parent.getRow() - 1;
124: Object modelChild = parent
125: .getModelChildNode(modelIndex);
126: return parent.getModelPath().pathByAddingChild(
127: modelChild);
128: }
129:
130: for (int i = 0; i < parent.getChildCount(); i++) {
131: VariableHeightStateNode childNode = (VariableHeightStateNode) parent
132: .get(i);
133: if (childNode.getRow() == row) {
134: return childNode.getModelPath();
135: }
136: if (childNode.getRow() > row) {
137: int modelChildIndex = parent
138: .getModelIndexOfChild(childNode
139: .getModelNode());
140: int modelIndex = modelChildIndex
141: - (childNode.getRow() - row);
142: Object modelChild = parent
143: .getModelChildNode(modelIndex);
144: return parent.getModelPath().pathByAddingChild(
145: modelChild);
146: }
147: if (childNode.getRow() < row
148: && childNode.getRow()
149: + childNode.getTotalChildrenCount() >= row) {
150: parent = childNode;
151: break;
152: }
153: if (i == parent.getChildCount() - 1) {
154: int modelChildIndex = parent
155: .getModelIndexOfChild(childNode
156: .getModelNode());
157: int modelIndex = modelChildIndex
158: + row
159: - (childNode.getRow() + childNode
160: .getTotalChildrenCount());
161: Object modelChild = parent
162: .getModelChildNode(modelIndex);
163: return parent.getModelPath().pathByAddingChild(
164: modelChild);
165: }
166: }
167: }
168: }
169:
170: public int getRowForPath(final TreePath path) {
171: if (!isVisible(path)) {
172: return -1;
173: }
174:
175: VariableHeightStateNode correspondingNode = (VariableHeightStateNode) getStateNodeForPath(path);
176: if (correspondingNode != null) {
177: return correspondingNode.getRow();
178: }
179:
180: VariableHeightStateNode parent = (VariableHeightStateNode) getStateNodeForPath(path
181: .getParentPath());
182:
183: int modelIndex = parent.getModelIndexOfChild(path
184: .getLastPathComponent());
185: int rowIncrement = 0;
186: for (int i = 0; i < modelIndex; i++) {
187: rowIncrement++;
188:
189: Object modelNode = parent.getModelChildNode(i);
190: StateNode childNode = parent.getChild(modelNode);
191: if (childNode != null) {
192: rowIncrement += childNode.getTotalChildrenCount();
193: }
194: }
195: return parent.getRow() + rowIncrement + 1;
196: }
197:
198: public int getRowCount() {
199: return getRowCountImpl();
200: }
201:
202: public void invalidatePathBounds(final TreePath path) {
203: StateNode node = getStateNodeForPath(path);
204: if (node == null) {
205: node = getStateNodeForPath(path.getParentPath());
206: }
207:
208: if (node != null) {
209: node.invalidateTreePartBelow();
210: }
211: }
212:
213: public int getPreferredHeight() {
214: if (getStateRoot() == null) {
215: return 0;
216: }
217:
218: if (isFixedRowHeight()) {
219: return getRowCount() * getRowHeight();
220: }
221:
222: VariableHeightStateNode root = (VariableHeightStateNode) getStateRoot();
223: return root.getTotalChildrenHeight()
224: + (isRootVisible() ? root.getHeight() : 0);
225: }
226:
227: public int getPreferredWidth(final Rectangle bounds) {
228: return super .getPreferredWidth(bounds);
229: }
230:
231: public TreePath getPathClosestTo(final int x, final int y) {
232: if (getStateRoot() == null) {
233: return null;
234: }
235:
236: if (isFixedRowHeight()) {
237: return getFixedHeightPathClosestToImpl(x, y);
238: }
239:
240: if (!getStateRoot().isExpanded()) {
241: return isRootVisible() ? getStateRoot().getModelPath()
242: : null;
243: }
244:
245: int cummulativeHeight = 0;
246: VariableHeightStateNode currentParent = (VariableHeightStateNode) getStateRoot();
247: if (isRootVisible()) {
248: if (currentParent.getHeight() > y) {
249: return currentParent.getModelPath();
250: }
251: cummulativeHeight += currentParent.getHeight();
252: }
253:
254: while (true) {
255: int modelChildCount = currentParent.getModelChildCount();
256: if (modelChildCount == 0) {
257: return currentParent.getModelPath();
258: }
259: for (int i = 0; i < modelChildCount; i++) {
260: if (cummulativeHeight
261: + currentParent.getChildrenHeights()[i] > y
262: || i + 1 == modelChildCount) {
263: Object modelChild = currentParent
264: .getModelChildNode(i);
265: VariableHeightStateNode childNode = (VariableHeightStateNode) currentParent
266: .getChild(modelChild);
267: if (childNode != null) {
268: currentParent = childNode;
269: if (cummulativeHeight
270: + currentParent.getHeight() > y
271: || !currentParent.isExpanded()) {
272:
273: return currentParent.getModelPath();
274: }
275: cummulativeHeight += currentParent.getHeight();
276: break;
277: }
278: return currentParent.getModelPath()
279: .pathByAddingChild(modelChild);
280: }
281: cummulativeHeight += currentParent.getChildrenHeights()[i];
282: }
283: }
284: }
285:
286: public Enumeration<TreePath> getVisiblePathsFrom(final TreePath path) {
287: return getVisiblePathsFromImpl(path);
288: }
289:
290: public int getVisibleChildCount(final TreePath path) {
291: return getVisibleChildCountImpl(path);
292: }
293:
294: public void invalidateSizes() {
295: if (getStateRoot() != null) {
296: getStateRoot().invalidateSubtree();
297: }
298: }
299:
300: public boolean isExpanded(final TreePath path) {
301: return getExpandedState(path);
302: }
303:
304: public void treeNodesChanged(final TreeModelEvent e) {
305: treeNodesChangedImpl(e);
306: }
307:
308: public void treeNodesInserted(final TreeModelEvent e) {
309: treeNodesInsertedImpl(e);
310: }
311:
312: public void treeNodesRemoved(final TreeModelEvent e) {
313: treeNodesRemovedImpl(e);
314: }
315:
316: public void treeStructureChanged(final TreeModelEvent e) {
317: treeStructureChangedImpl(e);
318: }
319:
320: class VariableHeightStateNode extends StateNode {
321: private int totalChildrenHeight;
322: private int height;
323: private int[] childrenHeights;
324:
325: private int row;
326:
327: public VariableHeightStateNode(final StateNode parent,
328: final TreePath path) {
329: super (parent, path);
330: }
331:
332: public int getTotalChildrenHeight() {
333: validate();
334: return totalChildrenHeight;
335: }
336:
337: public int[] getChildrenHeights() {
338: validate();
339: return childrenHeights;
340: }
341:
342: public int getHeight() {
343: validate();
344: return height;
345: }
346:
347: public int getRow() {
348: validate();
349: return row;
350: }
351:
352: protected void validateData() {
353: super .validateData();
354: if (getParent() == null) {
355: row = isRootVisible() ? 0 : -1;
356: }
357: Rectangle bounds = getNodeDimensions(getModelNode(), row,
358: getPathDepth(getModelPath()), isExpanded(), null);
359: height = bounds != null ? bounds.height : 0;
360:
361: if (!isExpanded()) {
362: totalChildrenCount = 0;
363: totalChildrenHeight = 0;
364:
365: if (childrenHeights == null
366: || childrenHeights.length != 0) {
367: childrenHeights = new int[0];
368: }
369: return;
370: }
371:
372: int modelChildCount = getModelChildCount();
373: int childRow = row;
374:
375: totalChildrenCount = 0;
376: totalChildrenHeight = 0;
377:
378: if (childrenHeights == null
379: || childrenHeights.length != modelChildCount) {
380: childrenHeights = new int[modelChildCount];
381: }
382: for (int i = 0; i < modelChildCount; i++) {
383: Object modelChild = getModelChildNode(i);
384: VariableHeightStateNode stateChild = (VariableHeightStateNode) getChild(modelChild);
385: childRow++;
386: if (stateChild != null) {
387: stateChild.row = childRow;
388: childrenHeights[i] = stateChild.getHeight()
389: + stateChild.getTotalChildrenHeight();
390: totalChildrenCount += stateChild
391: .getTotalChildrenCount();
392:
393: childRow += stateChild.getTotalChildrenCount();
394: } else {
395: bounds = getNodeDimensions(modelChild, childRow,
396: getPathDepth(getModelPath()) + 1, false,
397: bounds);
398: childrenHeights[i] = bounds != null ? bounds.height
399: : 0;
400: }
401: totalChildrenCount++;
402: totalChildrenHeight += childrenHeights[i];
403: }
404: }
405: }
406:
407: StateNode createStateNode(final StateNode parent,
408: final TreePath path) {
409: return new VariableHeightStateNode(parent, path);
410: }
411: }
|