diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java | |
download | cbb-gcc-4.6.4-upstream.tar.bz2 cbb-gcc-4.6.4-upstream.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java')
-rw-r--r-- | libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java | 628 |
1 files changed, 628 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java new file mode 100644 index 000000000..6ff2e3595 --- /dev/null +++ b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java @@ -0,0 +1,628 @@ +/* FixedHeightLayoutCache.java -- Fixed cell height tree layout cache +Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.tree; + +import gnu.javax.swing.tree.GnuPath; + +import java.awt.Rectangle; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.Set; +import java.util.Vector; + +import javax.swing.event.TreeModelEvent; + + +/** + * The fixed height tree layout. This class assumes that all cells in the tree + * have the same fixed height. This may be not the case, for instance, if leaves + * and branches have different height, of if the tree rows may have arbitrary + * variable height. This class will also work if the NodeDimensions are not + * set. + * + * @author Audrius Meskauskas + * @author Andrew Selkirk + */ +public class FixedHeightLayoutCache + extends VariableHeightLayoutCache +{ + /** + * The cached node record. + */ + class NodeRecord + { + NodeRecord(int aRow, int aDepth, Object aNode, Object aParent) + { + row = aRow; + depth = aDepth; + parent = aParent; + node = aNode; + + isExpanded = expanded.contains(aNode); + } + + /** + * The row, where the tree node is displayed. + */ + final int row; + + /** + * The nesting depth + */ + final int depth; + + /** + * The parent of the given node, null for the root node. + */ + final Object parent; + + /** + * This node. + */ + final Object node; + + /** + * True for the expanded nodes. The value is calculated in constructor. + * Using this field saves one hashtable access operation. + */ + final boolean isExpanded; + + /** + * The cached bounds of the tree row. + */ + Rectangle bounds; + + /** + * The path from the tree top to the given node (computed under first + * demand) + */ + private TreePath path; + + /** + * Get the path for this node. The derived class is returned, + * making check for the last child of some parent easier. + */ + TreePath getPath() + { + if (path == null) + { + boolean lastChild = false; + if (parent != null) + { + int nc = treeModel.getChildCount(parent); + if (nc > 0) + { + int n = treeModel.getIndexOfChild(parent, node); + if (n == nc - 1) + lastChild = true; + } + } + + LinkedList<Object> lpath = new LinkedList<Object>(); + NodeRecord rp = this; + while (rp != null) + { + lpath.addFirst(rp.node); + if (rp.parent != null) + { + Object parent = rp.parent; + rp = (NodeRecord) nodes.get(parent); + // Add the root node, even if it is not visible. + if (rp == null) + lpath.addFirst(parent); + } + else + rp = null; + } + path = new GnuPath(lpath.toArray(), lastChild); + } + return path; + } + + /** + * Get the rectangle bounds (compute, if required). + */ + Rectangle getBounds() + { + // This method may be called in the context when the tree rectangle is + // not known. To work around this, it is assumed near infinitely large. + if (bounds == null) + bounds = getNodeDimensions(node, row, depth, isExpanded, + new Rectangle()); + return bounds; + } + } + + /** + * The set of all expanded tree nodes. + */ + Set<Object> expanded = new HashSet<Object>(); + + /** + * Maps nodes to the row numbers. + */ + Hashtable<Object,NodeRecord> nodes = new Hashtable<Object,NodeRecord>(); + + /** + * Maps row numbers to nodes. + */ + Hashtable<Integer,Object> row2node = new Hashtable<Integer,Object>(); + + /** + * If true, the row map must be recomputed before using. + */ + boolean dirty; + + /** + * The cumulative height of all rows. + */ + int totalHeight; + + /** + * The maximal width. + */ + int maximalWidth; + + /** + * Creates the unitialised instance. Before using the class, the row height + * must be set with the {@link #setRowHeight(int)} and the model must be set + * with {@link #setModel(TreeModel)}. The node dimensions may not be set. + */ + public FixedHeightLayoutCache() + { + // Nothing to do here. + } + + /** + * Get the total number of rows in the tree. Every displayed node occupies the + * single row. The root node row is included if the root node is set as + * visible (false by default). + * + * @return int the number of the displayed rows. + */ + public int getRowCount() + { + if (dirty) update(); + return row2node.size(); + } + + /** + * Refresh the row map. + */ + private final void update() + { + nodes.clear(); + row2node.clear(); + + totalHeight = maximalWidth = 0; + + Object root = treeModel.getRoot(); + + if (rootVisible) + { + countRows(root, null, 0); + } + else + { + int sc = treeModel.getChildCount(root); + for (int i = 0; i < sc; i++) + { + Object child = treeModel.getChild(root, i); + countRows(child, root, 0); + } + } + dirty = false; + } + + /** + * Recursively counts all rows in the tree. + */ + private final void countRows(Object node, Object parent, int depth) + { + Integer n = new Integer(row2node.size()); + row2node.put(n, node); + + NodeRecord nr = new NodeRecord(n.intValue(), depth, node, parent); + nodes.put(node, nr); + + // For expanded nodes and for the root node. + if (expanded.contains(node)) + { + int sc = treeModel.getChildCount(node); + int deeper = depth + 1; + for (int i = 0; i < sc; i++) + { + Object child = treeModel.getChild(node, i); + countRows(child, node, deeper); + } + } + } + + /** + * Discard the bound information for the given path. + * + * @param path the path, for that the bound information must be recomputed. + */ + public void invalidatePathBounds(TreePath path) + { + NodeRecord r = (NodeRecord) nodes.get(path.getLastPathComponent()); + if (r != null) + r.bounds = null; + } + + /** + * Mark all cached information as invalid. + */ + public void invalidateSizes() + { + dirty = true; + } + + /** + * Set the expanded state of the given path. The expansion states must be + * always updated when expanding and colapsing the tree nodes. Otherwise + * other methods will not work correctly after the nodes are collapsed or + * expanded. + * + * @param path the tree path, for that the state is being set. + * @param isExpanded the expanded state of the given path. + */ + public void setExpandedState(TreePath path, boolean isExpanded) + { + if (isExpanded) + expanded.add(path.getLastPathComponent()); + else + expanded.remove(path.getLastPathComponent()); + + dirty = true; + } + + /** + * Get the expanded state for the given tree path. + * + * @return true if the given path is expanded, false otherwise. + */ + public boolean isExpanded(TreePath path) + { + return expanded.contains(path.getLastPathComponent()); + } + + /** + * Get bounds for the given tree path. + * + * @param path the tree path + * @param rect the rectangle that will be reused to return the result. + * @return Rectangle the bounds of the last line, defined by the given path. + */ + public Rectangle getBounds(TreePath path, Rectangle rect) + { + if (path == null) + return null; + if (dirty) + update(); + Object last = path.getLastPathComponent(); + NodeRecord r = nodes.get(last); + if (r == null) + // This node is not visible. + { + rect.x = rect.y = rect.width = rect.height = 0; + } + else + { + if (r.bounds == null) + { + Rectangle dim = getNodeDimensions(last, r.row, r.depth, + r.isExpanded, rect); + r.bounds = dim; + } + + rect.setRect(r.bounds); + } + return rect; + } + + /** + * Get the path, the last element of that is displayed in the given row. + * + * @param row the row + * @return TreePath the path + */ + public TreePath getPathForRow(int row) + { + if (dirty) + update(); + Object last = row2node.get(new Integer(row)); + if (last == null) + return null; + else + { + NodeRecord r = nodes.get(last); + return r.getPath(); + } + } + + /** + * Get the row, displaying the last node of the given path. + * + * @param path the path + * @return int the row number or -1 if the end of the path is not visible. + */ + public int getRowForPath(TreePath path) + { + if (path == null) + return -1; + + if (dirty) update(); + + NodeRecord r = nodes.get(path.getLastPathComponent()); + if (r == null) + return - 1; + else + return r.row; + } + + /** + * Get the path, closest to the given point. + * + * @param x the point x coordinate + * @param y the point y coordinate + * @return the tree path, closest to the the given point + */ + public TreePath getPathClosestTo(int x, int y) + { + if (dirty) + update(); + + // As the rows have arbitrary height, we need to iterate. + NodeRecord best = null; + NodeRecord r; + Enumeration<NodeRecord> en = nodes.elements(); + + int dist = Integer.MAX_VALUE; + + while (en.hasMoreElements() && dist > 0) + { + r = en.nextElement(); + if (best == null) + { + best = r; + dist = distance(r.getBounds(), x, y); + } + else + { + int rr = distance(r.getBounds(), x, y); + if (rr < dist) + { + best = r; + dist = rr; + } + } + } + + if (best == null) + return null; + else + return best.getPath(); + } + + /** + * Get the closest distance from this point till the given rectangle. Only + * vertical distance is taken into consideration. + */ + int distance(Rectangle r, int x, int y) + { + if (y < r.y) + return r.y - y; + else if (y > r.y + r.height) + return y - (r.y + r.height); + else + return 0; + } + + /** + * Get the number of the visible childs for the given tree path. If the node + * is not expanded, 0 is returned. Otherwise, the number of children is + * obtained from the model as the number of children for the last path + * component. + * + * @param path the tree path + * @return int the number of the visible childs (for row). + */ + public int getVisibleChildCount(TreePath path) + { + if (isExpanded(path)) + return 0; + else + return treeModel.getChildCount(path.getLastPathComponent()); + } + + /** + * Get the enumeration over all visible paths that start from the given + * parent path. + * + * @param parentPath the parent path + * @return the enumeration over pathes + */ + public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath) + { + if (dirty) + update(); + Vector<TreePath> p = new Vector<TreePath>(parentPath.getPathCount()); + Object node; + NodeRecord nr; + + for (int i = 0; i < parentPath.getPathCount(); i++) + { + node = parentPath.getPathComponent(i); + nr = nodes.get(node); + if (nr.row >= 0) + p.add((TreePath) node); + } + return p.elements(); + } + + /** + * Return the expansion state of the given tree path. The expansion state + * must be previously set with the + * {@link #setExpandedState(TreePath, boolean)} + * + * @param path the path being checked + * @return true if the last node of the path is expanded, false otherwise. + */ + public boolean getExpandedState(TreePath path) + { + return expanded.contains(path.getLastPathComponent()); + } + + /** + * The listener method, called when the tree nodes are changed. + * + * @param event the change event + */ + public void treeNodesChanged(TreeModelEvent event) + { + dirty = true; + } + + /** + * The listener method, called when the tree nodes are inserted. + * + * @param event the change event + */ + public void treeNodesInserted(TreeModelEvent event) + { + dirty = true; + } + + /** + * The listener method, called when the tree nodes are removed. + * + * @param event the change event + */ + public void treeNodesRemoved(TreeModelEvent event) + { + dirty = true; + } + + /** + * Called when the tree structure has been changed. + * + * @param event the change event + */ + public void treeStructureChanged(TreeModelEvent event) + { + dirty = true; + } + + /** + * Set the tree model that will provide the data. + */ + public void setModel(TreeModel newModel) + { + treeModel = newModel; + // The root node is expanded by default. + expanded.add(treeModel.getRoot()); + dirty = true; + } + + /** + * Inform the instance if the tree root node is visible. If this method + * is not called, it is assumed that the tree root node is not visible. + * + * @param visible true if the tree root node is visible, false + * otherwise. + */ + public void setRootVisible(boolean visible) + { + rootVisible = visible; + dirty = true; + } + + /** + * Get the sum of heights for all rows. + */ + public int getPreferredHeight() + { + if (dirty) + update(); + totalHeight = 0; + Enumeration<NodeRecord> en = nodes.elements(); + while (en.hasMoreElements()) + { + NodeRecord nr = en.nextElement(); + Rectangle r = nr.getBounds(); + totalHeight += r.height; + } + return totalHeight; + } + + /** + * Get the maximal width. + */ + public int getPreferredWidth(Rectangle value) + { + if (dirty) + update(); + + maximalWidth = 0; + Enumeration<NodeRecord> en = nodes.elements(); + while (en.hasMoreElements()) + { + NodeRecord nr = en.nextElement(); + Rectangle r = nr.getBounds(); + if (r.x + r.width > maximalWidth) + maximalWidth = r.x + r.width; + } + return maximalWidth; + } + + /** + * Returns true if this layout supposes that all rows have the fixed + * height. + * + * @return boolean true if all rows in the tree must have the fixed + * height (true by default). + */ + protected boolean isFixedRowHeight() + { + return true; + } + +} |