These classes are probably for use in JTree implementation, but our
JTree was somehow implemented without using them. The classes are
implemented anyway for the sake of compatibility or probably may be used
in the future.
2006-04-10 Audrius Meskauskas <[EMAIL PROTECTED]>
* javax/swing/tree/AbstractLayoutCache.java
(getNodeDimensions, getRowsForPath): Implemented.
* javax/swing/tree/FixedHeightLayoutCache.java: Rewritten.
* javax/swing/tree/VariableHeightLayoutCache.java: Rewritten.
Index: AbstractLayoutCache.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/tree/AbstractLayoutCache.java,v
retrieving revision 1.8
diff -u -r1.8 AbstractLayoutCache.java
--- AbstractLayoutCache.java 23 Mar 2006 01:53:37 -0000 1.8
+++ AbstractLayoutCache.java 10 Apr 2006 15:00:22 -0000
@@ -133,26 +133,25 @@
return nodeDimensions;
}
- /**
- * getNodeDimensions
- *
- * @param value TODO
- * @param row TODO
- * @param depth TODO
- * @param expanded TODO
- * @param bounds TODO
- *
- * @return Rectangle
- */
- protected Rectangle getNodeDimensions(Object value, int row, int depth,
- boolean expanded, Rectangle bounds)
- throws NotImplementedException
- {
- if (bounds == null)
- return new Rectangle();
- return null;
- // TODO
- }
+ /**
+ * Get the node dimensions. The NodeDimensions property must be set (unless
+ * the method is overridden, like if [EMAIL PROTECTED] FixedHeightLayoutCache}. If the
+ * method is not overridden and the property is not set, the InternalError is
+ * thrown.
+ *
+ * @param value the last node in the path
+ * @param row the node row
+ * @param depth the indentation depth
+ * @param expanded true if this node is expanded, false otherwise
+ * @param bounds the area where the tree is displayed
+ */
+ protected Rectangle getNodeDimensions(Object value, int row, int depth,
+ boolean expanded, Rectangle bounds)
+ {
+ if (nodeDimensions == null)
+ throw new InternalError("The NodeDimensions are not set");
+ return nodeDimensions.getNodeDimensions(value, row, depth, expanded, bounds);
+ }
/**
* Sets the model that provides the tree data.
@@ -391,16 +390,22 @@
public abstract void treeStructureChanged(TreeModelEvent event);
/**
- * getRowsForPaths
+ * Get the tree row numbers for the given pathes. This method performs
+ * the "bulk" conversion that may be faster than mapping pathes one
+ * by one. To have the benefit from the bulk conversion, the method
+ * must be overridden in the derived classes. The default method
+ * delegates work to the [EMAIL PROTECTED] #getRowForPath(TreePath)}.
*
- * @param paths the tree paths
+ * @param paths the tree paths the array of the tree pathes.
*
- * @return an array of rows
+ * @return the array of the matching tree rows.
*/
public int[] getRowsForPaths(TreePath[] paths)
- throws NotImplementedException
{
- return null; // TODO
+ int [] rows = new int[paths.length];
+ for (int i = 0; i < rows.length; i++)
+ rows [i] = getRowForPath(paths[i]);
+ return rows;
}
/**
Index: FixedHeightLayoutCache.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/tree/FixedHeightLayoutCache.java,v
retrieving revision 1.8
diff -u -r1.8 FixedHeightLayoutCache.java
--- FixedHeightLayoutCache.java 23 Mar 2006 01:53:38 -0000 1.8
+++ FixedHeightLayoutCache.java 10 Apr 2006 15:00:23 -0000
@@ -1,4 +1,4 @@
-/* FixedHeightLayoutCache.java --
+/* FixedHeightLayoutCache.java -- Fixed cell height tree layout cache
Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -37,215 +37,479 @@
package javax.swing.tree;
-import gnu.classpath.NotImplementedException;
-
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.UIManager;
import javax.swing.event.TreeModelEvent;
/**
- * FixedHeightLayoutCache
+ * 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. If they are set, the size calculations are just forwarded to the set
+ * instance.
*
- * @author Andrew Selkirk
+ * @author Audrius Meskauskas
+ * @author Andrew Selkirk
*/
public class FixedHeightLayoutCache
extends AbstractLayoutCache
{
+ /**
+ * 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;
+
+ TreePath getPath()
+ {
+ if (path == null)
+ {
+ LinkedList lpath = new LinkedList();
+ NodeRecord rp = this;
+ while (rp != null)
+ {
+ lpath.addFirst(rp.node);
+ if (rp.parent != null)
+ rp = (NodeRecord) nodes.get(rp.parent);
+ else
+ rp = null;
+ }
+ path = new TreePath(lpath.toArray());
+ }
+ return path;
+ }
+ }
/**
- * Constructor FixedHeightLayoutCache
+ * The set of all expanded tree nodes.
+ */
+ Set expanded = new HashSet();
+
+ /**
+ * Maps nodes to the row numbers.
+ */
+ Hashtable nodes = new Hashtable();
+
+ /**
+ * Maps row numbers to nodes.
+ */
+ Hashtable row2node = new Hashtable();
+
+ /**
+ * If true, the row map must be recomputed before using.
+ */
+ boolean dirty;
+
+ /**
+ * Creates the unitialised instance. Before using the class, the row height
+ * must be set with the [EMAIL PROTECTED] #setRowHeight(int)} and the model must be set
+ * with [EMAIL PROTECTED] #setModel(TreeModel)}. The node dimensions may not be set.
*/
public FixedHeightLayoutCache()
- throws NotImplementedException
{
- // TODO
+ // Nothing to do here.
}
/**
- * getRowCount
+ * 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
+ * @return int the number of the displayed rows.
+ */
+ public int getRowCount()
+ {
+ if (dirty) update();
+ return row2node.size();
+ }
+
+ /**
+ * Refresh the row map.
*/
- public int getRowCount()
- throws NotImplementedException
+ private final void update()
{
- return 0; // TODO
- }
+ nodes.clear();
+ row2node.clear();
+ 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;
+ }
+
/**
- * invalidatePathBounds
- *
- * @param value0 TODO
+ * Recursively counts all rows in the tree.
*/
- public void invalidatePathBounds(TreePath value0)
- throws NotImplementedException
+ private final void countRows(Object node, Object parent, int depth)
{
- // TODO
+ Integer n = new Integer(row2node.size());
+ row2node.put(n, node);
+
+ nodes.put(node, new NodeRecord(n.intValue(), depth, node, parent));
+
+ 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);
+ }
+ }
+ }
+
+ /**
+ * This should invalidate the width of the last path component, but
+ * following the JDK 1.4 API it is not cached and the method should return
+ * without action.
+ * @param path the path being invalidated, ignored.
+ */
+ public void invalidatePathBounds(TreePath path)
+ {
+ // Following JDK 1.4 API, should return without action.
}
/**
- * invalidateSizes
+ * Mark all cached information as invalid.
*/
public void invalidateSizes()
- throws NotImplementedException
{
- // TODO
+ dirty = true;
}
/**
- * isExpanded
+ * 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.
*
- * @param value0 TODO
- * @return boolean
+ * @return true if the given path is expanded, false otherwise.
*/
- public boolean isExpanded(TreePath value0)
- throws NotImplementedException
+ public boolean isExpanded(TreePath path)
{
- return false; // TODO
+ return expanded.contains(path.getLastPathComponent());
}
/**
- * getBounds
+ * Get bounds for the given tree path.
*
- * @param value0 TODO
- * @param value1 TODO
- * @return Rectangle
+ * @param path the tree path
+ * @param rect the rectangle, specifying the area where the path should be
+ * displayed.
+ * @return Rectangle the bounds of the last line, defined by the given path.
*/
- public Rectangle getBounds(TreePath value0, Rectangle value1)
- throws NotImplementedException
+ public Rectangle getBounds(TreePath path, Rectangle rect)
{
- return null; // TODO
+ if (dirty)
+ update();
+ Object last = path.getLastPathComponent();
+ NodeRecord r = (NodeRecord) nodes.get(last);
+ if (r == null)
+ // This node is not visible.
+ return new Rectangle();
+ else
+ {
+ if (r.bounds == null)
+ {
+ Rectangle dim = getNodeDimensions(last, r.row, r.depth, r.isExpanded,
+ rect);
+ r.bounds = dim;
+ }
+ return r.bounds;
+ }
}
/**
- * getPathForRow
+ * Get the path, the last element of that is displayed in the given row.
*
- * @param row TODO
- * @return TreePath
+ * @param row the row
+ * @return TreePath the path
*/
public TreePath getPathForRow(int row)
- throws NotImplementedException
{
- //TODO
- return null;
+ if (dirty)
+ update();
+ Object last = row2node.get(new Integer(row));
+ if (last == null)
+ return null;
+ else
+ {
+ NodeRecord r = (NodeRecord) nodes.get(last);
+ return r.getPath();
+ }
}
/**
- * getRowForPath
+ * Get the row, displaying the last node of the given path.
*
- * @param value0 TODO
- * @return int
+ * @param path the path
+ * @return int the row number or -1 if the end of the path is not visible.
*/
- public int getRowForPath(TreePath value0)
- throws NotImplementedException
+ public int getRowForPath(TreePath path)
{
- return 0;
+ if (dirty) update();
+
+ NodeRecord r = (NodeRecord) nodes.get(path.getLastPathComponent());
+ if (r == null)
+ return - 1;
+ else
+ return r.row;
}
/**
- * getPathClosestTo
+ * Get the path, closest to the given point.
*
- * @param value0 TODO
- * @param value1 TODO
- * @return TreePath
+ * @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 value0, int value1)
- throws NotImplementedException
+ public TreePath getPathClosestTo(int x, int y)
{
- return null; // TODO
+ if (dirty)
+ update();
+
+ // We do not need to iterate because all rows have the same height.
+ int row = y / rowHeight;
+ if (row < 0)
+ row = 0;
+ if (row > getRowCount())
+ row = getRowCount() - 1;
+
+ if (row < 0)
+ return null; // Empty tree - nothing to return.
+
+ Object node = row2node.get(new Integer(row));
+ NodeRecord nr = (NodeRecord) nodes.get(node);
+ return nr.getPath();
}
/**
- * getVisibleChildCount
+ * 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 value0 TODO
- * @return int
+ * @param path the tree path
+ * @return int the number of the visible childs (for row).
*/
- public int getVisibleChildCount(TreePath value0)
- throws NotImplementedException
+ public int getVisibleChildCount(TreePath path)
{
- return 0; // TODO
+ if (isExpanded(path))
+ return 0;
+ else
+ return treeModel.getChildCount(path.getLastPathComponent());
}
/**
- * getVisiblePathsFrom
+ * Get the enumeration over all visible pathes that start from the given
+ * parent path.
*
- * @param value0 TODO
- * @return Enumeration
+ * @param parentPath the parent path
+ * @return the enumeration over pathes
*/
- public Enumeration getVisiblePathsFrom(TreePath value0)
- throws NotImplementedException
+ public Enumeration getVisiblePathsFrom(TreePath parentPath)
{
- return null; // TODO
+ if (dirty)
+ update();
+ Vector p = new Vector(parentPath.getPathCount());
+ Object node;
+ NodeRecord nr;
+
+ for (int i = 0; i < parentPath.getPathCount(); i++)
+ {
+ node = parentPath.getPathComponent(i);
+ nr = (NodeRecord) nodes.get(node);
+ if (nr.row >= 0)
+ p.add(node);
+ }
+ return p.elements();
}
/**
- * setExpandedState
+ * Return the expansion state of the given tree path. The expansion state
+ * must be previously set with the
+ * [EMAIL PROTECTED] #setExpandedState(TreePath, boolean)}
*
- * @param value0 TODO
- * @param value1 TODO
+ * @param path the path being checked
+ * @return true if the last node of the path is expanded, false otherwise.
*/
- public void setExpandedState(TreePath value0, boolean value1)
- throws NotImplementedException
+ public boolean getExpandedState(TreePath path)
{
- // TODO
- }
+ return expanded.contains(path.getLastPathComponent());
+ }
/**
- * getExpandedState
+ * The listener method, called when the tree nodes are changed.
*
- * @param value0 TODO
- * @return boolean
+ * @param event the change event
*/
- public boolean getExpandedState(TreePath value0)
- throws NotImplementedException
+ public void treeNodesChanged(TreeModelEvent event)
{
- return false; // TODO
- }
+ dirty = true;
+ }
/**
- * treeNodesChanged
+ * The listener method, called when the tree nodes are inserted.
*
- * @param value0 TODO
+ * @param event the change event
*/
- public void treeNodesChanged(TreeModelEvent value0)
- throws NotImplementedException
+ public void treeNodesInserted(TreeModelEvent event)
{
- // TODO
+ dirty = true;
}
/**
- * treeNodesInserted
+ * The listener method, called when the tree nodes are removed.
*
- * @param value0 TODO
+ * @param event the change event
*/
- public void treeNodesInserted(TreeModelEvent value0)
- throws NotImplementedException
+ public void treeNodesRemoved(TreeModelEvent event)
{
- // TODO
+ dirty = true;
}
/**
- * treeNodesRemoved
+ * Called when the tree structure has been changed.
*
- * @param value0 TODO
+ * @param event the change event
*/
- public void treeNodesRemoved(TreeModelEvent value0)
- throws NotImplementedException
+ public void treeStructureChanged(TreeModelEvent event)
{
- // TODO
+ dirty = true;
}
-
+
+ /**
+ * Set the tree model that will provide the data.
+ */
+ public void setModel(TreeModel newModel)
+ {
+ super.setModel(newModel);
+ dirty = true;
+ }
+
/**
- * treeStructureChanged
+ * 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 value0 TODO
+ * @param visible true if the tree root node is visible, false
+ * otherwise.
*/
- public void treeStructureChanged(TreeModelEvent value0)
- throws NotImplementedException
+ public void setRootVisible(boolean visible)
{
- // TODO
- }
+ rootVisible = visible;
+ dirty = true;
+ }
+ /**
+ * Get the node dimensions. If the NodeDimensions are not set, this method
+ * calculates dimensions assuming the fixed row height.
+ *
+ * @param value the last node in the path
+ * @param row the node row
+ * @param depth the indentation depth
+ * @param expanded true if this node is expanded, false otherwise
+ * @param bounds the area where the tree is displayed
+ */
+ protected Rectangle getNodeDimensions(Object value, int row, int depth,
+ boolean expanded, Rectangle bounds)
+ {
+ if (nodeDimensions != null)
+ return nodeDimensions.getNodeDimensions(value, row, depth, expanded,
+ bounds);
+ else
+ {
+ Rectangle r = new Rectangle(bounds);
+
+ int indent = depth * UIManager.getInt("Tree.rightChildIndent");
+
+ r.x = indent;
+ r.y = row * getRowHeight();
+ r.width = bounds.width = r.x;
+ r.height = getRowHeight();
+ return r;
+ }
+ }
}
Index: VariableHeightLayoutCache.java
===================================================================
RCS file: /sources/classpath/classpath/javax/swing/tree/VariableHeightLayoutCache.java,v
retrieving revision 1.7
diff -u -r1.7 VariableHeightLayoutCache.java
--- VariableHeightLayoutCache.java 23 Mar 2006 01:53:38 -0000 1.7
+++ VariableHeightLayoutCache.java 10 Apr 2006 15:00:24 -0000
@@ -35,251 +35,492 @@
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.classpath.NotImplementedException;
-
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;
/**
- * VariableHeightLayoutCache
- * @author Andrew Selkirk
+ * The fixed height tree layout. This class requires the NodeDimensions to be
+ * set and ignores the row height property.
+ *
+ * @author Audrius Meskauskas
+ * @author Andrew Selkirk
*/
-public class VariableHeightLayoutCache extends AbstractLayoutCache
+public class VariableHeightLayoutCache
+ extends AbstractLayoutCache
{
-
/**
- * Constructor VariableHeightLayoutCache
+ * The cached node record.
*/
- public VariableHeightLayoutCache()
- throws NotImplementedException
+ class NodeRecord
{
- // TODO
- }
-
+ 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;
+
+ TreePath getPath()
+ {
+ if (path == null)
+ {
+ LinkedList lpath = new LinkedList();
+ NodeRecord rp = this;
+ while (rp != null)
+ {
+ lpath.addFirst(rp.node);
+ if (rp.parent != null)
+ rp = (NodeRecord) nodes.get(rp.parent);
+ else
+ rp = null;
+ }
+ path = new TreePath(lpath.toArray());
+ }
+ 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(Integer.MAX_VALUE/2,
+ Integer.MAX_VALUE/2));
+ return bounds;
+ }
+ }
+
/**
- * setModel
- * @param value0 TODO
+ * The set of all expanded tree nodes.
*/
- public void setModel(TreeModel value0)
- throws NotImplementedException
- {
- // TODO
- }
-
+ Set expanded = new HashSet();
+
/**
- * setRootVisible
- * @param value0 TODO
+ * Maps nodes to the row numbers.
*/
- public void setRootVisible(boolean value0)
- throws NotImplementedException
- {
- // TODO
- }
-
+ Hashtable nodes = new Hashtable();
+
/**
- * setNodeDimensions
- * @param value0 TODO
+ * Maps row numbers to nodes.
*/
- public void setNodeDimensions(NodeDimensions value0)
- throws NotImplementedException
- {
- // TODO
- }
-
+ Hashtable row2node = new Hashtable();
+
/**
- * setExpandedState
- * @param value0 TODO
- * @param value1 TODO
+ * If true, the row map must be recomputed before using.
*/
- public void setExpandedState(TreePath value0, boolean value1)
- throws NotImplementedException
- {
- // TODO
- }
+ boolean dirty;
/**
- * getExpandedState
- * @param value0 TODO
- * @return boolean
+ * Creates the unitialised instance. Before using the class, the row height
+ * must be set with the [EMAIL PROTECTED] #setRowHeight(int)} and the model must be set
+ * with [EMAIL PROTECTED] #setModel(TreeModel)}. The node dimensions may not be set.
*/
- public boolean getExpandedState(TreePath value0)
- throws NotImplementedException
+ public VariableHeightLayoutCache()
{
- return false; // TODO
+ // Nothing to do here.
}
/**
- * getBounds
- * @param value0 TODO
- * @param value1 TODO
- * @return Rectangle
+ * 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 Rectangle getBounds(TreePath value0, Rectangle value1)
- throws NotImplementedException
+ public int getRowCount()
{
- return null; // TODO
+ if (dirty) update();
+ return row2node.size();
}
-
+
/**
- * getPathForRow
- * @param value0 TODO
- * @return TreePath
+ * Refresh the row map.
*/
- public TreePath getPathForRow(int value0)
- throws NotImplementedException
+ private final void update()
{
- return null; // TODO
- }
+ nodes.clear();
+ row2node.clear();
+
+ 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;
+ }
+
/**
- * getRowForPath
- * @param value0 TODO
- * @return int
+ * Recursively counts all rows in the tree.
*/
- public int getRowForPath(TreePath value0)
- throws NotImplementedException
+ private final void countRows(Object node, Object parent, int depth)
{
- return 0; // TODO
- }
+ Integer n = new Integer(row2node.size());
+ row2node.put(n, node);
+
+ nodes.put(node, new NodeRecord(n.intValue(), depth, node, parent));
+
+ 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);
+ }
+ }
+ }
/**
- * getRowCount
- * @return int
+ * Discard the bound information for the given path.
+ *
+ * @param path the path, for that the bound information must be recomputed.
*/
- public int getRowCount()
- throws NotImplementedException
+ public void invalidatePathBounds(TreePath path)
{
- return 0; // TODO
+ NodeRecord r = (NodeRecord) nodes.get(path.getLastPathComponent());
+ if (r!=null)
+ r.bounds = null;
}
/**
- * invalidatePathBounds
- * @param value0 TODO
+ * Mark all cached information as invalid.
*/
- public void invalidatePathBounds(TreePath value0)
- throws NotImplementedException
+ public void invalidateSizes()
{
- // TODO
+ dirty = true;
}
/**
- * getPreferredHeight
- * @return int
+ * 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 int getPreferredHeight()
- throws NotImplementedException
+ public void setExpandedState(TreePath path, boolean isExpanded)
{
- return 0; // TODO
+ if (isExpanded)
+ expanded.add(path.getLastPathComponent());
+ else
+ expanded.remove(path.getLastPathComponent());
+
+ dirty = true;
}
-
+
/**
- * getPreferredWidth
- * @param value0 TODO
- * @return int
- */
- public int getPreferredWidth(Rectangle value0)
- throws NotImplementedException
- {
- return 0; // TODO
- }
+ * 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, specifying the area where the path should be
+ * displayed.
+ * @return Rectangle the bounds of the last line, defined by the given path.
+ */
+ public Rectangle getBounds(TreePath path, Rectangle rect)
+ {
+ if (dirty)
+ update();
+ Object last = path.getLastPathComponent();
+ NodeRecord r = (NodeRecord) nodes.get(last);
+ if (r == null)
+ // This node is not visible.
+ return new Rectangle();
+ else
+ {
+ if (r.bounds == null)
+ {
+ Rectangle dim = getNodeDimensions(last, r.row, r.depth,
+ r.isExpanded, rect);
+ r.bounds = dim;
+ }
+ return r.bounds;
+ }
+ }
+
+ /**
+ * 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 = (NodeRecord) 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 (dirty) update();
+
+ NodeRecord r = (NodeRecord) 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 en = nodes.elements();
+
+ int dist = Integer.MAX_VALUE;
+
+ while (en.hasMoreElements() && dist > 0)
+ {
+ r = (NodeRecord) 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;
+ }
/**
- * getPathClosestTo
- * @param value0 TODO
- * @param value1 TODO
- * @return TreePath
- */
- public TreePath getPathClosestTo(int value0, int value1)
- throws NotImplementedException
- {
- return null; // TODO
+ * 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());
}
/**
- * getVisiblePathsFrom
- * @param value0 TODO
- * @return Enumeration
- */
- public Enumeration getVisiblePathsFrom(TreePath value0)
- throws NotImplementedException
- {
- return null; // TODO
- }
+ * Get the enumeration over all visible pathes that start from the given
+ * parent path.
+ *
+ * @param parentPath the parent path
+ * @return the enumeration over pathes
+ */
+ public Enumeration getVisiblePathsFrom(TreePath parentPath)
+ {
+ if (dirty)
+ update();
+ Vector p = new Vector(parentPath.getPathCount());
+ Object node;
+ NodeRecord nr;
+
+ for (int i = 0; i < parentPath.getPathCount(); i++)
+ {
+ node = parentPath.getPathComponent(i);
+ nr = (NodeRecord) nodes.get(node);
+ if (nr.row >= 0)
+ p.add(node);
+ }
+ return p.elements();
+ }
/**
- * getVisibleChildCount
- * @param value0 TODO
- * @return int
+ * Return the expansion state of the given tree path. The expansion state
+ * must be previously set with the
+ * [EMAIL PROTECTED] #setExpandedState(TreePath, boolean)}
+ *
+ * @param path the path being checked
+ * @return true if the last node of the path is expanded, false otherwise.
*/
- public int getVisibleChildCount(TreePath value0)
- throws NotImplementedException
+ public boolean getExpandedState(TreePath path)
{
- return 0; // TODO
- }
+ return expanded.contains(path.getLastPathComponent());
+ }
/**
- * invalidateSizes
+ * The listener method, called when the tree nodes are changed.
+ *
+ * @param event the change event
*/
- public void invalidateSizes()
- throws NotImplementedException
+ public void treeNodesChanged(TreeModelEvent event)
{
- // TODO
- }
+ dirty = true;
+ }
/**
- * isExpanded
- * @param value0 TODO
- * @return boolean
+ * The listener method, called when the tree nodes are inserted.
+ *
+ * @param event the change event
*/
- public boolean isExpanded(TreePath value0)
- throws NotImplementedException
+ public void treeNodesInserted(TreeModelEvent event)
{
- return false; // TODO
+ dirty = true;
}
/**
- * treeNodesChanged
- * @param value0 TODO
+ * The listener method, called when the tree nodes are removed.
+ *
+ * @param event the change event
*/
- public void treeNodesChanged(TreeModelEvent value0)
- throws NotImplementedException
+ public void treeNodesRemoved(TreeModelEvent event)
{
- // TODO
+ dirty = true;
}
/**
- * treeNodesInserted
- * @param value0 TODO
+ * Called when the tree structure has been changed.
+ *
+ * @param event the change event
*/
- public void treeNodesInserted(TreeModelEvent value0)
- throws NotImplementedException
+ public void treeStructureChanged(TreeModelEvent event)
{
- // TODO
+ dirty = true;
}
-
+
/**
- * treeNodesRemoved
- * @param value0 TODO
+ * Set the tree model that will provide the data.
*/
- public void treeNodesRemoved(TreeModelEvent value0)
- throws NotImplementedException
+ public void setModel(TreeModel newModel)
{
- // TODO
- }
-
+ treeModel = newModel;
+ dirty = true;
+ }
+
/**
- * treeStructureChanged
- * @param value0 TODO
+ * 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 treeStructureChanged(TreeModelEvent value0)
- throws NotImplementedException
+ public void setRootVisible(boolean visible)
{
- // TODO
- }
-
+ rootVisible = visible;
+ dirty = true;
+ }
}