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/VariableHeightLayoutCache.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/VariableHeightLayoutCache.java')
-rw-r--r-- | libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java new file mode 100644 index 000000000..0887061b3 --- /dev/null +++ b/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java @@ -0,0 +1,657 @@ +/* VariableHeightLayoutCache.java -- + 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.ArrayList; +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 requires the NodeDimensions to be + * set and ignores the row height property. + * + * @specnote the methods, of this class, returning TreePath, actually returns + * the derived class GnuPath that provides additional information for optimized + * painting. See the GnuPath code for details. + * + * @author Audrius Meskauskas + */ +public class VariableHeightLayoutCache + extends AbstractLayoutCache +{ + + private static final Rectangle RECT_CACHE = new Rectangle(); + + /** + * 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); + bounds = new Rectangle(0, -1, 0, 0); + } + + /** + * 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 = 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() + { + 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. + */ + ArrayList<Object> row2node = new ArrayList<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 VariableHeightLayoutCache() + { + // 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; + + if (treeModel == null) + return; + + Object root = treeModel.getRoot(); + countRows(root, null, 0, 0); + dirty = false; + } + + /** + * Recursively counts all rows in the tree. + */ + private final int countRows(Object node, Object parent, int depth, int y) + { + boolean visible = node != treeModel.getRoot() || rootVisible; + int row = row2node.size(); + if (visible) + { + row2node.add(node); + } + NodeRecord nr = new NodeRecord(row, depth, node, parent); + NodeDimensions d = getNodeDimensions(); + Rectangle r = RECT_CACHE; + if (d != null) + r = d.getNodeDimensions(node, row, depth, nr.isExpanded, r); + else + r.setBounds(0, 0, 0, 0); + + if (! visible) + r.y = -1; + else + r.y = Math.max(0, y); + + if (isFixedRowHeight()) + r.height = getRowHeight(); + + nr.bounds.setBounds(r); + nodes.put(node, nr); + + if (visible) + y += r.height; + + int sc = treeModel.getChildCount(node); + int deeper = depth + 1; + if (expanded.contains(node)) + { + for (int i = 0; i < sc; i++) + { + Object child = treeModel.getChild(node, i); + y = countRows(child, node, deeper, y); + } + } + return y; + } + + /** + * 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 = 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) + { + int length = path.getPathCount(); + for (int i = 0; i < length; i++) + expanded.add(path.getPathComponent(i)); + } + 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(); + Rectangle result = null; + NodeRecord r = nodes.get(last); + if (r != null) + { + // The RI allows null arguments for rect, in which case a new Rectangle + // is created. + result = rect; + if (result == null) + result = new Rectangle(r.bounds); + else + result.setBounds(r.bounds); + } + return result; + } + + /** + * 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(); + + TreePath path = null; + // Search row in the nodes map. TODO: This is inefficient, optimize this. + Enumeration<NodeRecord> nodesEnum = nodes.elements(); + while (nodesEnum.hasMoreElements() && path == null) + { + NodeRecord record = nodesEnum.nextElement(); + if (record.row == row) + path = record.getPath(); + } + return path; + } + + /** + * 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 - 1) + return y - (r.y + r.height - 1); + 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) || treeModel == null) + 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 != null && 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; + dirty = true; + if (treeModel != null) + { + // The root node is expanded by default. + expanded.add(treeModel.getRoot()); + } + } + + /** + * 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(); + int height = 0; + int rowCount = getRowCount(); + if (rowCount > 0) + { + NodeRecord last = nodes.get(row2node.get(rowCount - 1)); + height = last.bounds.y + last.bounds.height; + } + return height; + } + + /** + * 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(); + if (nr != null) + { + Rectangle r = nr.getBounds(); + int width = r.x + r.width; + if (width > maximalWidth) + maximalWidth = width; + } + } + return maximalWidth; + } + + /** + * Sets the node dimensions and invalidates the cached layout. + * + * @param dim the dimensions to set + */ + public void setNodeDimensions(NodeDimensions dim) + { + super.setNodeDimensions(dim); + dirty = true; + } + + /** + * Sets the row height and marks the layout as invalid. + * + * @param height the row height to set + */ + public void setRowHeight(int height) + { + super.setRowHeight(height); + dirty = true; + } +} |