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 Alexander T. Simbirtsev, Anton Avtamonov
019: * @version $Revision$
020: */package org.apache.harmony.x.swing;
021:
022: import java.awt.Graphics;
023: import java.awt.Insets;
024: import java.awt.Rectangle;
025: import java.util.Enumeration;
026: import java.util.Hashtable;
027:
028: import javax.swing.JComponent;
029: import javax.swing.JTree;
030: import javax.swing.SwingUtilities;
031: import javax.swing.tree.AbstractLayoutCache;
032: import javax.swing.tree.TreeNode;
033: import javax.swing.tree.TreePath;
034:
035: /**
036: * Storage of the utility methods for tree-related calculations.
037: *
038: */
039: public class TreeCommons {
040: /**
041: * Painting context. Used by
042: * {@link TreeCommons#paintTree(Graphics, TreeCommons.PaintTreeContext)}
043: * method to parameterize painting differences of Basic and Metal L&Fs.
044: */
045: public interface PaintTreeContext {
046: /**
047: * @return Painting tree
048: */
049: JTree getTree();
050:
051: /**
052: * @return Drawing cache used for painting optimization
053: */
054: Hashtable getDrawingCache();
055:
056: /**
057: * @return Layout cache used by the tree UI
058: */
059: AbstractLayoutCache getLayoutCache();
060:
061: /**
062: * @return <code>true</code> if root handles should be drawn; <code>false</code> otherwise
063: */
064: boolean paintHandles();
065:
066: /**
067: * @return <code>true</code> if horizontal children separators should be drawn; <code>false</code> otherwise
068: */
069: boolean paintHorizontalSeparators();
070:
071: /**
072: * Determines if the specified path is being edited.
073: *
074: * @param path TreePath to be checked against being edited or not
075: *
076: * @return <code>true</code> if the specified path is edited; <code>false</code> otherwise
077: */
078: boolean isEditing(TreePath path);
079:
080: /**
081: * Draws vertical tree leg.
082: *
083: * @param g Graphics to paint on
084: * @param clipBounds Rectangle representing current Graphics's clip
085: * @param insets Insets of the tree (tree's border)
086: * @param path TreePath for which vertical leg should be drawn
087: */
088: void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
089: Insets insets, TreePath path);
090:
091: /**
092: * Draws horizontal tree leg.
093: *
094: * @param g Graphics to paint on
095: * @param clipBounds Rectangle representing current Graphics's clip
096: * @param insets Insets of the tree (tree's border)
097: * @param path TreePath for which vertical leg should be drawn
098: * @param row int value representing painting row
099: * @param isExpanded boolean value representing of current row is expanded
100: * @param hasBeenExpanded boolean value representing of current row has been ever expanded
101: * @param isLeaf boolean value representing of current row is leaf
102: */
103: void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
104: Insets insets, Rectangle bounds, TreePath path,
105: int row, boolean isExpanded, boolean hasBeenExpanded,
106: boolean isLeaf);
107:
108: /**
109: * Draws expand control (control which being clicked expand/collapse row).
110: *
111: * @param g Graphics to paint on
112: * @param clipBounds Rectangle representing current Graphics's clip
113: * @param insets Insets of the tree (tree's border)
114: * @param path TreePath for which vertical leg should be drawn
115: * @param row int value representing painting row
116: * @param isExpanded boolean value representing of current row is expanded
117: * @param hasBeenExpanded boolean value representing of current row has been ever expanded
118: * @param isLeaf boolean value representing of current row is leaf
119: */
120: void paintExpandControl(Graphics g, Rectangle clipBounds,
121: Insets insets, Rectangle bounds, TreePath path,
122: int row, boolean isExpanded, boolean hasBeenExpanded,
123: boolean isLeaf);
124:
125: /**
126: * Draws the row.
127: *
128: * @param g Graphics to paint on
129: * @param clipBounds Rectangle representing current Graphics's clip
130: * @param insets Insets of the tree (tree's border)
131: * @param path TreePath for which vertical leg should be drawn
132: * @param row int value representing painting row
133: * @param isExpanded boolean value representing of current row is expanded
134: * @param hasBeenExpanded boolean value representing of current row has been ever expanded
135: * @param isLeaf boolean value representing of current row is leaf
136: */
137: void paintRow(Graphics g, Rectangle clipBounds, Insets insets,
138: Rectangle bounds, TreePath path, int row,
139: boolean isExpanded, boolean hasBeenExpanded,
140: boolean isLeaf);
141:
142: /**
143: * Draws horizontal separators. Is called if paintHorizontalSeparators() returns <code>true</code>.
144: *
145: * @param g Graphics to paint on
146: * @param tree JTree to be painted
147: */
148: void paintHorizontalSeparators(Graphics g, JComponent tree);
149: }
150:
151: /**
152: * Returns tree path from the specified ancestor to a node.
153: *
154: * @param node TreeNode which is the path end
155: * @param ancestor TreeNode which is the path top
156: *
157: * @return path from an ancestor to a node
158: */
159: public static TreeNode[] getPathToAncestor(final TreeNode node,
160: final TreeNode ancestor) {
161: return getPathToAncestor(node, ancestor, 0);
162: }
163:
164: /**
165: * Returns tree path from the specified ancestor to a node limited by the depth.
166: *
167: * @param node TreeNode which is the path end
168: * @param ancestor TreeNode which is the path top
169: * @param depth int value representing the maximum path length
170: *
171: * @return path from an ancestor to a node
172: */
173: public static TreeNode[] getPathToAncestor(final TreeNode node,
174: final TreeNode ancestor, final int depth) {
175: if (node == null) {
176: return new TreeNode[depth];
177: }
178:
179: if (node == ancestor) {
180: TreeNode[] result = new TreeNode[depth + 1];
181: result[0] = ancestor;
182: return result;
183: }
184:
185: TreeNode[] result = getPathToAncestor(node.getParent(),
186: ancestor, depth + 1);
187: result[result.length - depth - 1] = node;
188: return result;
189: }
190:
191: /**
192: * Paints a tree basing on the data from the parameterized context.
193: *
194: * @param g Graphics to paint on
195: * @param context PaintTreeContext specified by particular UI.
196: * @see TreeCommons.PaintTreeContext
197: */
198: public static void paintTree(final Graphics g,
199: final PaintTreeContext context) {
200: JTree tree = context.getTree();
201: Insets insets = tree.getInsets();
202:
203: Rectangle clipBounds = g.getClipBounds();
204: if (clipBounds == null) {
205: clipBounds = SwingUtilities.getLocalBounds(tree);
206: }
207: TreePath startPath = tree.getClosestPathForLocation(
208: clipBounds.x, clipBounds.y);
209: if (startPath == null) {
210: return;
211: }
212: TreePath endPath = tree.getClosestPathForLocation(clipBounds.x
213: + clipBounds.width, clipBounds.y + clipBounds.height);
214:
215: if (context.getDrawingCache().isEmpty()) {
216: Enumeration expanded = tree
217: .getExpandedDescendants(new TreePath(tree
218: .getModel().getRoot()));
219: while (expanded.hasMoreElements()) {
220: TreePath path = (TreePath) expanded.nextElement();
221: context.paintVerticalPartOfLeg(g, clipBounds, insets,
222: path);
223: context.getDrawingCache().put(path, Boolean.TRUE);
224: }
225: } else {
226: Enumeration keys = context.getDrawingCache().keys();
227: while (keys.hasMoreElements()) {
228: TreePath path = (TreePath) keys.nextElement();
229: context.paintVerticalPartOfLeg(g, clipBounds, insets,
230: path);
231: }
232: }
233:
234: Enumeration paths = context.getLayoutCache()
235: .getVisiblePathsFrom(startPath);
236: while (paths.hasMoreElements()) {
237: TreePath path = (TreePath) paths.nextElement();
238:
239: int rowForPath = tree.getRowForPath(path);
240: Rectangle bounds = tree.getPathBounds(path);
241: boolean isExpanded = tree.isExpanded(path);
242: boolean hasBeenExpanded = tree.hasBeenExpanded(path);
243: boolean isLeaf = tree.getModel().isLeaf(
244: path.getLastPathComponent());
245:
246: if (context.paintHandles()) {
247: context.paintHorizontalPartOfLeg(g, clipBounds, insets,
248: bounds, path, rowForPath, isExpanded,
249: hasBeenExpanded, isLeaf);
250: if (isExpanded) {
251: context.paintVerticalPartOfLeg(g, clipBounds,
252: insets, path);
253: }
254: }
255: context.paintExpandControl(g, clipBounds, insets, bounds,
256: path, rowForPath, isExpanded, hasBeenExpanded,
257: isLeaf);
258: if (!context.isEditing(path)) {
259: context
260: .paintRow(g, clipBounds, insets, bounds, path,
261: rowForPath, isExpanded,
262: hasBeenExpanded, isLeaf);
263: }
264:
265: if (endPath.equals(path)) {
266: break;
267: }
268: }
269: if (context.paintHorizontalSeparators()) {
270: context.paintHorizontalSeparators(g, tree);
271: }
272: }
273: }
|