summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/javax/swing/tree
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/tree')
-rw-r--r--libjava/classpath/javax/swing/tree/AbstractLayoutCache.java27
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java12
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java322
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java108
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeModel.java29
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java780
-rw-r--r--libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java2
-rw-r--r--libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java190
8 files changed, 880 insertions, 590 deletions
diff --git a/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java b/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java
index 155343f5bcc..4a6899fbeae 100644
--- a/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java
+++ b/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java
@@ -149,9 +149,11 @@ public abstract class AbstractLayoutCache
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);
+ Rectangle d = null;
+ if (nodeDimensions != null)
+ d = nodeDimensions.getNodeDimensions(value, row, depth, expanded,
+ bounds);
+ return d;
}
/**
@@ -224,7 +226,12 @@ public abstract class AbstractLayoutCache
*/
public void setSelectionModel(TreeSelectionModel model)
{
+ if (treeSelectionModel != null)
+ treeSelectionModel.setRowMapper(null);
treeSelectionModel = model;
+ if (treeSelectionModel != null)
+ treeSelectionModel.setRowMapper(this);
+
}
/**
@@ -337,7 +344,7 @@ public abstract class AbstractLayoutCache
*
* @return Enumeration
*/
- public abstract Enumeration getVisiblePathsFrom(TreePath path);
+ public abstract Enumeration<TreePath> getVisiblePathsFrom(TreePath path);
/**
* getVisibleChildCount
@@ -425,9 +432,13 @@ public abstract class AbstractLayoutCache
*/
public int[] getRowsForPaths(TreePath[] paths)
{
- int[] rows = new int[paths.length];
- for (int i = 0; i < rows.length; i++)
- rows[i] = getRowForPath(paths[i]);
+ int[] rows = null;
+ if (paths != null)
+ {
+ rows = new int[paths.length];
+ for (int i = 0; i < rows.length; i++)
+ rows[i] = getRowForPath(paths[i]);
+ }
return rows;
}
@@ -440,6 +451,6 @@ public abstract class AbstractLayoutCache
*/
protected boolean isFixedRowHeight()
{
- return false;
+ return rowHeight > 0;
}
}
diff --git a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
index 6951b960005..9f587946fc2 100644
--- a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
+++ b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
@@ -67,7 +67,7 @@ public class DefaultMutableTreeNode
* An empty enumeration, returned by {@link #children()} if a node has no
* children.
*/
- public static final Enumeration EMPTY_ENUMERATION =
+ public static final Enumeration<TreeNode> EMPTY_ENUMERATION =
EmptyEnumeration.getInstance();
/**
@@ -78,7 +78,7 @@ public class DefaultMutableTreeNode
/**
* The child nodes for this node (may be empty).
*/
- protected Vector children = new Vector();
+ protected Vector<MutableTreeNode> children = new Vector<MutableTreeNode>();
/**
* userObject
@@ -480,7 +480,7 @@ public class DefaultMutableTreeNode
public TreeNode getSharedAncestor(DefaultMutableTreeNode node)
{
TreeNode current = this;
- ArrayList list = new ArrayList();
+ ArrayList<TreeNode> list = new ArrayList<TreeNode>();
while (current != null)
{
@@ -527,7 +527,7 @@ public class DefaultMutableTreeNode
|| children.size() == 0)
return 0;
- Stack stack = new Stack();
+ Stack<Integer> stack = new Stack<Integer>();
stack.push(new Integer(0));
TreeNode node = getChildAt(0);
int depth = 0;
@@ -765,7 +765,7 @@ public class DefaultMutableTreeNode
throw new IllegalArgumentException();
TreeNode parent = this;
- Vector nodes = new Vector();
+ Vector<TreeNode> nodes = new Vector<TreeNode>();
nodes.add(this);
while (parent != node && parent != null)
@@ -1148,7 +1148,7 @@ public class DefaultMutableTreeNode
static class PostorderEnumeration implements Enumeration
{
- Stack nodes = new Stack();
+ Stack<TreeNode> nodes = new Stack<TreeNode>();
Stack childrenEnums = new Stack();
PostorderEnumeration(TreeNode node)
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
index b0a4d8db823..4c10bfe1af2 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
@@ -43,7 +43,6 @@ import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
-import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
@@ -59,10 +58,10 @@ import javax.swing.Icon;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
+import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.CellEditorListener;
-import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
@@ -77,12 +76,6 @@ public class DefaultTreeCellEditor
implements ActionListener, TreeCellEditor, TreeSelectionListener
{
/**
- * The number of the fast mouse clicks, required to start the editing
- * session.
- */
- static int CLICK_COUNT_TO_START = 3;
-
- /**
* This container that appears on the tree during editing session.
* It contains the editing component displays various other editor -
* specific parts like editing icon.
@@ -99,7 +92,7 @@ public class DefaultTreeCellEditor
*/
public EditorContainer()
{
- // Do nothing here.
+ setLayout(null);
}
/**
@@ -111,12 +104,6 @@ public class DefaultTreeCellEditor
// Do nothing here.
}
- public void setBounds(Rectangle bounds)
- {
- super.setBounds(bounds);
- doLayout();
- }
-
/**
* Overrides Container.paint to paint the node's icon and use the selection
* color for the background.
@@ -126,11 +113,20 @@ public class DefaultTreeCellEditor
*/
public void paint(Graphics g)
{
+ // Paint editing icon.
if (editingIcon != null)
{
// From the previous version, the left margin is taken as half
// of the icon width.
- editingIcon.paintIcon(this, g, 0, 0);
+ int y = Math.max(0, (getHeight() - editingIcon.getIconHeight()) / 2);
+ editingIcon.paintIcon(this, g, 0, y);
+ }
+ // Paint border.
+ Color c = getBorderSelectionColor();
+ if (c != null)
+ {
+ g.setColor(c);
+ g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
super.paint(g);
}
@@ -141,27 +137,33 @@ public class DefaultTreeCellEditor
*/
public void doLayout()
{
- // The offset of the editing component.
- int eOffset;
+ if (editingComponent != null)
+ {
+ editingComponent.getPreferredSize();
+ editingComponent.setBounds(offset, 0, getWidth() - offset,
+ getHeight());
+ }
+ }
- // Move the component to the left, leaving room for the editing icon:
- if (editingIcon != null)
- eOffset = editingIcon.getIconWidth();
+ public Dimension getPreferredSize()
+ {
+ Dimension dim;
+ if (editingComponent != null)
+ {
+ dim = editingComponent.getPreferredSize();
+ dim.width += offset + 5;
+ if (renderer != null)
+ {
+ Dimension r = renderer.getPreferredSize();
+ dim.height = Math.max(dim.height, r.height);
+ }
+ if (editingIcon != null)
+ dim.height = Math.max(dim.height, editingIcon.getIconHeight());
+ dim.width = Math.max(100, dim.width);
+ }
else
- eOffset = 0;
-
- Rectangle bounds = getBounds();
- Component c = getComponent(0);
- c.setLocation(eOffset, 0);
-
- // Span the editing component near over all window width.
- c.setSize(bounds.width - eOffset, bounds.height);
- /*
- * @specnote the Sun sets some more narrow editing component width (it is
- * not documented how does it is calculated). However as our text field is
- * still not able to auto - scroll horizontally, replicating such strategy
- * would prevent adding extra characters to the text being edited.
- */
+ dim = new Dimension(0, 0);
+ return dim;
}
}
@@ -227,46 +229,15 @@ public class DefaultTreeCellEditor
*/
public Dimension getPreferredSize()
{
- String s = getText();
-
- Font f = getFont();
-
- if (f != null)
+ Dimension size = super.getPreferredSize();
+ if (renderer != null && DefaultTreeCellEditor.this.getFont() == null)
{
- FontMetrics fm = getToolkit().getFontMetrics(f);
-
- return new Dimension(SwingUtilities.computeStringWidth(fm, s),
- fm.getHeight());
+ size.height = renderer.getPreferredSize().height;
}
return renderer.getPreferredSize();
}
}
- /**
- * Listens for the events from the realEditor.
- */
- class RealEditorListener implements CellEditorListener
- {
- /**
- * The method is called when the editing has been cancelled.
- * @param event unused
- */
- public void editingCanceled(ChangeEvent event)
- {
- cancelCellEditing();
- }
-
- /**
- * The method is called after completing the editing session.
- *
- * @param event unused
- */
- public void editingStopped(ChangeEvent event)
- {
- stopCellEditing();
- }
- }
-
private EventListenerList listenerList = new EventListenerList();
/**
@@ -367,21 +338,14 @@ public class DefaultTreeCellEditor
public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer,
TreeCellEditor editor)
{
- setTree(tree);
this.renderer = renderer;
-
- if (editor == null)
- editor = createTreeCellEditor();
- else
- editor.addCellEditorListener(new RealEditorListener());
-
realEditor = editor;
-
- lastPath = tree.getLeadSelectionPath();
- tree.addTreeSelectionListener(this);
+ if (realEditor == null)
+ realEditor = createTreeCellEditor();
editingContainer = createContainer();
- setFont(UIManager.getFont("Tree.font"));
- setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
+ setTree(tree);
+ Color c = UIManager.getColor("Tree.editorBorderSelectionColor");
+ setBorderSelectionColor(c);
}
/**
@@ -505,19 +469,36 @@ public class DefaultTreeCellEditor
* @return the component for editing
*/
public Component getTreeCellEditorComponent(JTree tree, Object value,
- boolean isSelected, boolean expanded,
+ boolean isSelected,
+ boolean expanded,
boolean leaf, int row)
{
- if (realEditor == null)
- realEditor = createTreeCellEditor();
-
- return realEditor.getTreeCellEditorComponent(tree, value, isSelected,
- expanded, leaf, row);
+ setTree(tree);
+ lastRow = row;
+ determineOffset(tree, value, isSelected, expanded, leaf, row);
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+
+ editingComponent = realEditor.getTreeCellEditorComponent(tree, value,
+ isSelected,
+ expanded, leaf,
+ row);
+ Font f = getFont();
+ if (f == null)
+ {
+ if (renderer != null)
+ f = renderer.getFont();
+ if (f == null)
+ f = tree.getFont();
+ }
+ editingContainer.setFont(f);
+ prepareForEditing();
+ return editingContainer;
}
/**
* Returns the value currently being edited (requests it from the
- * {@link realEditor}.
+ * {@link #realEditor}.
*
* @return the value currently being edited
*/
@@ -535,16 +516,48 @@ public class DefaultTreeCellEditor
* @return true if editing can be started
*/
public boolean isCellEditable(EventObject event)
- {
- if (editingComponent == null)
- configureEditingComponent(tree, renderer, realEditor);
-
- if (editingComponent != null && realEditor.isCellEditable(event))
+ {
+ boolean ret = false;
+ boolean ed = false;
+ if (event != null)
{
- prepareForEditing();
- return true;
+ if (event.getSource() instanceof JTree)
+ {
+ setTree((JTree) event.getSource());
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ TreePath path = tree.getPathForLocation(me.getX(), me.getY());
+ ed = lastPath != null && path != null && lastPath.equals(path);
+ if (path != null)
+ {
+ lastRow = tree.getRowForPath(path);
+ Object val = path.getLastPathComponent();
+ boolean isSelected = tree.isRowSelected(lastRow);
+ boolean isExpanded = tree.isExpanded(path);
+ TreeModel m = tree.getModel();
+ boolean isLeaf = m.isLeaf(val);
+ determineOffset(tree, val, isSelected, isExpanded, isLeaf,
+ lastRow);
+ }
+ }
+ }
}
- return false;
+ if (! realEditor.isCellEditable(event))
+ ret = false;
+ else
+ {
+ if (canEditImmediately(event))
+ ret = true;
+ else if (ed && shouldStartEditingTimer(event))
+ startEditingTimer();
+ else if (timer != null && timer.isRunning())
+ timer.stop();
+ }
+ if (ret)
+ prepareForEditing();
+ return ret;
+
}
/**
@@ -567,14 +580,13 @@ public class DefaultTreeCellEditor
*/
public boolean stopCellEditing()
{
- if (editingComponent != null)
+ boolean ret = false;
+ if (realEditor.stopCellEditing())
{
- stopEditingTimer();
- tree.stopEditing();
- editingComponent = null;
- return true;
+ finish();
+ ret = true;
}
- return false;
+ return ret;
}
/**
@@ -583,21 +595,15 @@ public class DefaultTreeCellEditor
*/
public void cancelCellEditing()
{
- if (editingComponent != null)
- {
- tree.cancelEditing();
- editingComponent = null;
- }
- stopEditingTimer();
+ realEditor.cancelCellEditing();
+ finish();
}
-
- /**
- * Stop the editing timer, if it is installed and running.
- */
- private void stopEditingTimer()
+
+ private void finish()
{
- if (timer != null && timer.isRunning())
- timer.stop();
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+ editingComponent = null;
}
/**
@@ -640,10 +646,18 @@ public class DefaultTreeCellEditor
*/
public void valueChanged(TreeSelectionEvent e)
{
- tPath = lastPath;
- lastPath = e.getNewLeadSelectionPath();
- lastRow = tree.getRowForPath(lastPath);
- stopCellEditing();
+ if (tree != null)
+ {
+ if (tree.getSelectionCount() == 1)
+ lastPath = tree.getSelectionPath();
+ else
+ lastPath = null;
+ }
+ // TODO: We really should do the following here, but can't due
+ // to buggy DefaultTreeSelectionModel. This selection model
+ // should only fire if the selection actually changes.
+// if (timer != null)
+// timer.stop();
}
/**
@@ -653,6 +667,8 @@ public class DefaultTreeCellEditor
*/
public void actionPerformed(ActionEvent e)
{
+ if (tree != null && lastPath != null)
+ tree.startEditingAtPath(lastPath);
}
/**
@@ -664,7 +680,17 @@ public class DefaultTreeCellEditor
*/
protected void setTree(JTree newTree)
{
- tree = newTree;
+ if (tree != newTree)
+ {
+ if (tree != null)
+ tree.removeTreeSelectionListener(this);
+ tree = newTree;
+ if (tree != null)
+ tree.addTreeSelectionListener(this);
+
+ if (timer != null)
+ timer.stop();
+ }
}
/**
@@ -675,10 +701,14 @@ public class DefaultTreeCellEditor
*/
protected boolean shouldStartEditingTimer(EventObject event)
{
- if ((event instanceof MouseEvent) &&
- ((MouseEvent) event).getClickCount() == 1)
- return true;
- return false;
+ boolean ret = false;
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ ret = SwingUtilities.isLeftMouseButton(me) && me.getClickCount() == 1
+ && inHitRegion(me.getX(), me.getY());
+ }
+ return ret;
}
/**
@@ -686,8 +716,12 @@ public class DefaultTreeCellEditor
*/
protected void startEditingTimer()
{
- if (timer != null)
- timer.start();
+ if (timer == null)
+ {
+ timer = new Timer(1200, this);
+ timer.setRepeats(false);
+ }
+ timer.start();
}
/**
@@ -723,7 +757,6 @@ public class DefaultTreeCellEditor
protected boolean inHitRegion(int x, int y)
{
Rectangle bounds = tree.getPathBounds(lastPath);
-
return bounds.contains(x, y);
}
@@ -739,13 +772,24 @@ public class DefaultTreeCellEditor
protected void determineOffset(JTree tree, Object value, boolean isSelected,
boolean expanded, boolean leaf, int row)
{
- renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded,
- leaf, row, true);
- Icon c = renderer.getIcon();
- if (c != null)
- offset = renderer.getIconTextGap() + c.getIconWidth();
+ if (renderer != null)
+ {
+ if (leaf)
+ editingIcon = renderer.getLeafIcon();
+ else if (expanded)
+ editingIcon = renderer.getOpenIcon();
+ else
+ editingIcon = renderer.getClosedIcon();
+ if (editingIcon != null)
+ offset = renderer.getIconTextGap() + editingIcon.getIconWidth();
+ else
+ offset = renderer.getIconTextGap();
+ }
else
- offset = 0;
+ {
+ editingIcon = null;
+ offset = 0;
+ }
}
/**
@@ -754,8 +798,8 @@ public class DefaultTreeCellEditor
*/
protected void prepareForEditing()
{
- editingContainer.removeAll();
- editingContainer.add(editingComponent);
+ if (editingComponent != null)
+ editingContainer.add(editingComponent);
}
/**
@@ -776,10 +820,10 @@ public class DefaultTreeCellEditor
*/
protected TreeCellEditor createTreeCellEditor()
{
- DefaultCellEditor editor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField(
- UIManager.getBorder("Tree.selectionBorder")));
- editor.addCellEditorListener(new RealEditorListener());
- editor.setClickCountToStart(CLICK_COUNT_TO_START);
+ Border border = UIManager.getBorder("Tree.editorBorder");
+ JTextField tf = new DefaultTreeCellEditor.DefaultTextField(border);
+ DefaultCellEditor editor = new DefaultCellEditor(tf);
+ editor.setClickCountToStart(1);
realEditor = editor;
return editor;
}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
index e120b71c167..3766485abdb 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
@@ -77,7 +77,7 @@ public class DefaultTreeCellRenderer
protected boolean hasFocus;
/**
- * drawsFocusBorderAroundIcon // FIXME: is this used?
+ * Indicates if the focus border is also drawn around the icon.
*/
private boolean drawsFocusBorderAroundIcon;
@@ -152,6 +152,8 @@ public class DefaultTreeCellRenderer
setBackgroundNonSelectionColor(UIManager.getColor("Tree.textBackground"));
setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground"));
setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
+ Object val = UIManager.get("Tree.drawsFocusBorderAroundIcon");
+ drawsFocusBorderAroundIcon = val != null && ((Boolean) val).booleanValue();
}
/**
@@ -499,67 +501,75 @@ public class DefaultTreeCellRenderer
*/
public void paint(Graphics g)
{
- // paint background
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
-
- Insets insets = new Insets(0, 0, 0, 0);
- Border border = UIManager.getBorder("Tree.selectionBorder");
- if (border != null)
- insets = border.getBorderInsets(this);
-
- FontMetrics fm = getToolkit().getFontMetrics(getFont());
- SwingUtilities.layoutCompoundLabel((JLabel) this, fm, getText(),
- getIcon(), getVerticalAlignment(),
- getHorizontalAlignment(),
- getVerticalTextPosition(),
- getHorizontalTextPosition(), vr, ir, tr,
- getIconTextGap());
-
- // Reusing one rectangle.
- Rectangle bounds = getBounds(ir);
-
- bounds.x = tr.x - insets.left;
- bounds.width = tr.width + insets.left + insets.right;
-
- g.setColor(super.getBackground());
- g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ // Determine background color.
+ Color bgColor;
+ if (selected)
+ bgColor = getBackgroundSelectionColor();
+ else
+ {
+ bgColor = getBackgroundNonSelectionColor();
+ if (bgColor == null)
+ bgColor = getBackground();
+ }
+ // Paint background.
+ int xOffset = -1;
+ if (bgColor != null)
+ {
+ Icon i = getIcon();
+ xOffset = getXOffset();
+ g.setColor(bgColor);
+ g.fillRect(xOffset, 0, getWidth() - xOffset, getHeight());
+ }
- super.paint(g);
-
- // Paint the border of the focused element only (lead selection)
if (hasFocus)
{
- Color b = getBorderSelectionColor();
- if (b != null)
- {
- g.setColor(b);
- g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height - 1);
- }
+ if (drawsFocusBorderAroundIcon)
+ xOffset = 0;
+ else if (xOffset == -1)
+ xOffset = getXOffset();
+ paintFocus(g, xOffset, 0, getWidth() - xOffset, getHeight());
+ }
+ super.paint(g);
+ }
+
+ /**
+ * Paints the focus indicator.
+ */
+ private void paintFocus(Graphics g, int x, int y, int w, int h)
+ {
+ Color col = getBorderSelectionColor();
+ if (col != null)
+ {
+ g.setColor(col);
+ g.drawRect(x, y, w - 1, h - 1);
}
}
/**
+ * Determines the X offset of the label that is caused by
+ * the icon.
+ *
+ * @return the X offset of the label
+ */
+ private int getXOffset()
+ {
+ Icon i = getIcon();
+ int offs = 0;
+ if (i != null && getText() != null)
+ offs = i.getIconWidth() + Math.max(0, getIconTextGap() - 1);
+ return offs;
+ }
+
+ /**
* Returns the preferred size of the cell.
*
* @return The preferred size of the cell.
*/
public Dimension getPreferredSize()
{
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
-
- FontMetrics fm = getToolkit().getFontMetrics(getFont());
- SwingUtilities.layoutCompoundLabel((JLabel) this, fm, getText(),
- getIcon(), getVerticalAlignment(),
- getHorizontalAlignment(),
- getVerticalTextPosition(),
- getHorizontalTextPosition(), vr, ir, tr,
- getIconTextGap());
- Rectangle cr = ir.union(tr);
- return new Dimension(cr.width, cr.height);
+ Dimension size = super.getPreferredSize();
+ size.width += 3;
+ return size;
}
/**
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
index 5819d15b627..afee7ea22fa 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
@@ -210,17 +210,32 @@ public class DefaultTreeModel
}
/**
- * isLeaf
+ * Returns if the specified node is a leaf or not. When
+ * {@link #asksAllowsChildren} is true, then this checks if the TreeNode
+ * allows children, otherwise it returns the TreeNode's <code>leaf</code>
+ * property.
*
- * @param node TODO
- * @return boolean
+ * @param node the node to check
+ *
+ * @return boolean <code>true</code> if the node is a leaf node,
+ * <code>false</code> otherwise
+ *
+ * @throws ClassCastException if the specified node is not a
+ * <code>TreeNode</code> instance
+ *
+ * @see TreeNode#getAllowsChildren()
+ * @see TreeNode#isLeaf()
*/
public boolean isLeaf(Object node)
{
- if (node instanceof TreeNode)
- return ((TreeNode) node).isLeaf();
+ // The RI throws a ClassCastException when node isn't a TreeNode, so do we.
+ TreeNode treeNode = (TreeNode) node;
+ boolean leaf;
+ if (asksAllowsChildren)
+ leaf = ! treeNode.getAllowsChildren();
else
- return true;
+ leaf = treeNode.isLeaf();
+ return leaf;
}
/**
@@ -600,7 +615,7 @@ public class DefaultTreeModel
*
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java
index 0684ef76659..3d9c67728bd 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java
@@ -44,6 +44,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Iterator;
@@ -67,7 +68,39 @@ import javax.swing.event.TreeSelectionListener;
public class DefaultTreeSelectionModel
implements Cloneable, Serializable, TreeSelectionModel
{
-
+
+ /**
+ * According to the API docs, the method
+ * {@link DefaultTreeSelectionModel#notifyPathChange} should
+ * expect instances of a class PathPlaceHolder in the Vector parameter.
+ * This seems to be a non-public class, so I can only make guesses about the
+ * use of it.
+ */
+ private static class PathPlaceHolder
+ {
+ /**
+ * The path that we wrap.
+ */
+ TreePath path;
+
+ /**
+ * Indicates if the path is new or already in the selection.
+ */
+ boolean isNew;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param p the path to wrap
+ * @param n if the path is new or already in the selection
+ */
+ PathPlaceHolder(TreePath p, boolean n)
+ {
+ path = p;
+ isNew = n;
+ }
+ }
+
/**
* Use serialVersionUID for interoperability.
*/
@@ -124,12 +157,36 @@ public class DefaultTreeSelectionModel
protected int leadRow = -1;
/**
+ * A supporting datastructure that is used in addSelectionPaths() and
+ * removeSelectionPaths(). It contains currently selected paths.
+ *
+ * @see #addSelectionPaths(TreePath[])
+ * @see #removeSelectionPaths(TreePath[])
+ * @see #setSelectionPaths(TreePath[])
+ */
+ private transient HashSet selectedPaths;
+
+ /**
+ * A supporting datastructure that is used in addSelectionPaths() and
+ * removeSelectionPaths(). It contains the paths that are added or removed.
+ *
+ * @see #addSelectionPaths(TreePath[])
+ * @see #removeSelectionPaths(TreePath[])
+ * @see #setSelectionPaths(TreePath[])
+ */
+ private transient HashSet tmpPaths;
+
+ /**
* Constructs a new DefaultTreeSelectionModel.
*/
public DefaultTreeSelectionModel()
{
setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
+ listSelectionModel = new DefaultListSelectionModel();
listenerList = new EventListenerList();
+ leadIndex = -1;
+ tmpPaths = new HashSet();
+ selectedPaths = new HashSet();
}
/**
@@ -144,12 +201,14 @@ public class DefaultTreeSelectionModel
{
DefaultTreeSelectionModel cloned =
(DefaultTreeSelectionModel) super.clone();
-
- // Clone the selection and the list selection model.
+ cloned.changeSupport = null;
cloned.selection = (TreePath[]) selection.clone();
- if (listSelectionModel != null)
- cloned.listSelectionModel
- = (DefaultListSelectionModel) listSelectionModel.clone();
+ cloned.listenerList = new EventListenerList();
+ cloned.listSelectionModel =
+ (DefaultListSelectionModel) listSelectionModel.clone();
+ cloned.selectedPaths = new HashSet();
+ cloned.tmpPaths = new HashSet();
+
return cloned;
}
@@ -209,6 +268,7 @@ public class DefaultTreeSelectionModel
public void setRowMapper(RowMapper mapper)
{
rowMapper = mapper;
+ resetRowSelection();
}
/**
@@ -236,8 +296,18 @@ public class DefaultTreeSelectionModel
*/
public void setSelectionMode(int mode)
{
+ int oldMode = selectionMode;
selectionMode = mode;
- insureRowContinuity();
+ // Make sure we have a valid selection mode.
+ if (selectionMode != SINGLE_TREE_SELECTION
+ && selectionMode != CONTIGUOUS_TREE_SELECTION
+ && selectionMode != DISCONTIGUOUS_TREE_SELECTION)
+ selectionMode = DISCONTIGUOUS_TREE_SELECTION;
+
+ // Fire property change event.
+ if (oldMode != selectionMode && changeSupport != null)
+ changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, oldMode,
+ selectionMode);
}
/**
@@ -262,32 +332,10 @@ public class DefaultTreeSelectionModel
*/
public void setSelectionPath(TreePath path)
{
- // The most frequently only one cell in the tree is selected.
- TreePath[] ose = selection;
- selection = new TreePath[] { path };
- TreePath oldLead = leadPath;
- leadIndex = 0;
- leadRow = getRow(path);
- leadPath = path;
-
- TreeSelectionEvent event;
-
- if (ose != null && ose.length > 0)
- {
- // The first item in the path list is the selected path.
- // The remaining items are unselected pathes.
- TreePath[] changed = new TreePath[ose.length + 1];
- boolean[] news = new boolean[changed.length];
- news[0] = true;
- changed[0] = path;
- System.arraycopy(ose, 0, changed, 1, ose.length);
- event = new TreeSelectionEvent(this, changed, news, oldLead, path);
- }
- else
- {
- event = new TreeSelectionEvent(this, path, true, oldLead, path);
- }
- fireValueChanged(event);
+ TreePath[] paths = null;
+ if (path != null)
+ paths = new TreePath[]{ path };
+ setSelectionPaths(paths);
}
/**
@@ -307,7 +355,7 @@ public class DefaultTreeSelectionModel
AbstractLayoutCache ama = (AbstractLayoutCache) mapper;
return ama.getRowForPath(path);
}
- else
+ else if (mapper != null)
{
// Generic non optimized implementation.
int[] rows = mapper.getRowsForPaths(new TreePath[] { path });
@@ -316,6 +364,7 @@ public class DefaultTreeSelectionModel
else
return rows[0];
}
+ return -1;
}
/**
@@ -327,10 +376,90 @@ public class DefaultTreeSelectionModel
*/
public void setSelectionPaths(TreePath[] paths)
{
- // Must be called, as defined in JDK API 1.4.
- insureUniqueness();
- clearSelection();
- addSelectionPaths(paths);
+ int oldLength = 0;
+ if (selection != null)
+ oldLength = selection.length;
+ int newLength = 0;
+ if (paths != null)
+ newLength = paths.length;
+ if (newLength > 0 || oldLength > 0)
+ {
+ // For SINGLE_TREE_SELECTION and for CONTIGUOUS_TREE_SELECTION with
+ // a non-contiguous path, we only allow the first path element.
+ if ((selectionMode == SINGLE_TREE_SELECTION && newLength > 1)
+ || (selectionMode == CONTIGUOUS_TREE_SELECTION && newLength > 0
+ && ! arePathsContiguous(paths)))
+ {
+ paths = new TreePath[] { paths[0] };
+ newLength = 1;
+ }
+ // Find new paths.
+ Vector changedPaths = null;
+ tmpPaths.clear();
+ int validPaths = 0;
+ TreePath oldLeadPath = leadPath;
+ for (int i = 0; i < newLength; i++)
+ {
+ if (paths[i] != null && ! tmpPaths.contains(paths[i]))
+ {
+ validPaths++;
+ tmpPaths.add(paths[i]);
+ if (! selectedPaths.contains(paths[i]))
+ {
+ if (changedPaths == null)
+ changedPaths = new Vector();
+ changedPaths.add(new PathPlaceHolder(paths[i], true));
+ }
+ leadPath = paths[i];
+ }
+ }
+ // Put together the new selection.
+ TreePath[] newSelection = null;
+ if (validPaths != 0)
+ {
+ if (validPaths != newLength)
+ {
+ // Some of the paths are already selected, put together
+ // the new selection carefully.
+ newSelection = new TreePath[validPaths];
+ Iterator newPaths = tmpPaths.iterator();
+ validPaths = 0;
+ for (int i = 0; newPaths.hasNext(); i++)
+ newSelection[i] = (TreePath) newPaths.next();
+ }
+ else
+ {
+ newSelection = new TreePath[paths.length];
+ System.arraycopy(paths, 0, newSelection, 0, paths.length);
+ }
+ }
+
+ // Find paths that have been selected, but are no more.
+ for (int i = 0; i < oldLength; i++)
+ {
+ if (selection[i] != null && ! tmpPaths.contains(selection[i]))
+ {
+ if (changedPaths == null)
+ changedPaths = new Vector();
+ changedPaths.add(new PathPlaceHolder(selection[i], false));
+ }
+ }
+
+ // Perform changes and notification.
+ selection = newSelection;
+ HashSet tmp = selectedPaths;
+ selectedPaths = tmpPaths;
+ tmpPaths = tmp;
+ tmpPaths.clear();
+
+ // Not necessary, but required according to the specs and to tests.
+ if (selection != null)
+ insureUniqueness();
+ updateLeadIndex();
+ resetRowSelection();
+ if (changedPaths != null && changedPaths.size() > 0)
+ notifyPathChange(changedPaths, oldLeadPath);
+ }
}
/**
@@ -345,29 +474,10 @@ public class DefaultTreeSelectionModel
*/
public void addSelectionPath(TreePath path)
{
- if (! isPathSelected(path))
+ if (path != null)
{
- if (selectionMode == SINGLE_TREE_SELECTION || isSelectionEmpty()
- || ! canPathBeAdded(path))
- setSelectionPath(path);
- else
- {
- TreePath[] temp = new TreePath[selection.length + 1];
- System.arraycopy(selection, 0, temp, 0, selection.length);
- temp[temp.length - 1] = path;
- selection = new TreePath[temp.length];
- System.arraycopy(temp, 0, selection, 0, temp.length);
- }
- }
-
- if (path != leadPath)
- {
- TreePath oldLead = leadPath;
- leadPath = path;
- leadRow = getRow(path);
- leadIndex = selection.length - 1;
- fireValueChanged(new TreeSelectionEvent(this, path, true, oldLead,
- leadPath));
+ TreePath[] add = new TreePath[]{ path };
+ addSelectionPaths(add);
}
}
@@ -380,37 +490,76 @@ public class DefaultTreeSelectionModel
*/
public void addSelectionPaths(TreePath[] paths)
{
- // Must be called, as defined in JDK API 1.4.
- insureUniqueness();
-
- if (paths != null)
+ int length = paths != null ? paths.length : 0;
+ if (length > 0)
{
- TreePath v0 = null;
- for (int i = 0; i < paths.length; i++)
+ if (selectionMode == SINGLE_TREE_SELECTION)
+ setSelectionPaths(paths);
+ else if (selectionMode == CONTIGUOUS_TREE_SELECTION
+ && ! canPathsBeAdded(paths))
+ {
+ if (arePathsContiguous(paths))
+ setSelectionPaths(paths);
+ else
+ setSelectionPaths(new TreePath[] { paths[0] });
+ }
+ else
{
- v0 = paths[i];
- if (! isPathSelected(v0))
+ Vector changedPaths = null;
+ tmpPaths.clear();
+ int validPaths = 0;
+ TreePath oldLeadPath = leadPath;
+ int oldPaths = 0;
+ if (selection != null)
+ oldPaths = selection.length;
+ int i;
+ for (i = 0; i < length; i++)
{
- if (isSelectionEmpty())
- setSelectionPath(v0);
- else
+ if (paths[i] != null)
{
- TreePath[] temp = new TreePath[selection.length + 1];
- System.arraycopy(selection, 0, temp, 0, selection.length);
- temp[temp.length - 1] = v0;
- selection = new TreePath[temp.length];
- System.arraycopy(temp, 0, selection, 0, temp.length);
+ if (! selectedPaths.contains(paths[i]))
+ {
+ validPaths++;
+ if (changedPaths == null)
+ changedPaths = new Vector();
+ changedPaths.add(new PathPlaceHolder(paths[i], true));
+ selectedPaths.add(paths[i]);
+ tmpPaths.add(paths[i]);
+ }
+ leadPath = paths[i];
}
- TreePath oldLead = leadPath;
- leadPath = paths[paths.length - 1];
- leadRow = getRow(leadPath);
- leadIndex = selection.length - 1;
-
- fireValueChanged(new TreeSelectionEvent(this, v0, true,
- oldLead, leadPath));
}
+ if (validPaths > 0)
+ {
+ TreePath[] newSelection = new TreePath[oldPaths + validPaths];
+ if (oldPaths > 0)
+ System.arraycopy(selection, 0, newSelection, 0, oldPaths);
+ if (validPaths != paths.length)
+ {
+ // Some of the paths are already selected, put together
+ // the new selection carefully.
+ Iterator newPaths = tmpPaths.iterator();
+ i = oldPaths;
+ while (newPaths.hasNext())
+ {
+ newSelection[i] = (TreePath) newPaths.next();
+ i++;
+ }
+ }
+ else
+ System.arraycopy(paths, 0, newSelection, oldPaths,
+ validPaths);
+ selection = newSelection;
+ insureUniqueness();
+ updateLeadIndex();
+ resetRowSelection();
+ if (changedPaths != null && changedPaths.size() > 0)
+ notifyPathChange(changedPaths, oldLeadPath);
+ }
+ else
+ leadPath = oldLeadPath;
+ tmpPaths.clear();
}
- insureRowContinuity();
}
}
@@ -422,36 +571,8 @@ public class DefaultTreeSelectionModel
*/
public void removeSelectionPath(TreePath path)
{
- if (isSelectionEmpty())
- return;
-
- int index = - 1;
- if (isPathSelected(path))
- {
- for (int i = 0; i < selection.length; i++)
- {
- if (selection[i].equals(path))
- {
- index = i;
- break;
- }
- }
- TreePath[] temp = new TreePath[selection.length - 1];
- System.arraycopy(selection, 0, temp, 0, index);
- System.arraycopy(selection, index + 1, temp, index, selection.length
- - index - 1);
- selection = new TreePath[temp.length];
- System.arraycopy(temp, 0, selection, 0, temp.length);
-
- // If the removed path was the lead path, set the lead path to null.
- TreePath oldLead = leadPath;
- if (path != null && leadPath != null && path.equals(leadPath))
- leadPath = null;
-
- fireValueChanged(new TreeSelectionEvent(this, path, false, oldLead,
- leadPath));
- insureRowContinuity();
- }
+ if (path != null)
+ removeSelectionPaths(new TreePath[]{ path });
}
/**
@@ -462,40 +583,54 @@ public class DefaultTreeSelectionModel
*/
public void removeSelectionPaths(TreePath[] paths)
{
- if (isSelectionEmpty())
- return;
- if (paths != null)
+ if (paths != null && selection != null && paths.length > 0)
{
- int index = - 1;
- TreePath v0 = null;
- TreePath oldLead = leadPath;
- for (int i = 0; i < paths.length; i++)
+ if (! canPathsBeRemoved(paths))
+ clearSelection();
+ else
{
- v0 = paths[i];
- if (isPathSelected(v0))
+ Vector pathsToRemove = null;
+ for (int i = paths.length - 1; i >= 0; i--)
{
- for (int x = 0; x < selection.length; x++)
+ if (paths[i] != null && selectedPaths.contains(paths[i]))
{
- if (selection[i].equals(v0))
- {
- index = x;
- break;
- }
- if (leadPath != null && leadPath.equals(v0))
+ if (pathsToRemove == null)
+ pathsToRemove = new Vector();
+ selectedPaths.remove(paths[i]);
+ pathsToRemove.add(new PathPlaceHolder(paths[i],
+ false));
+ }
+ }
+ if (pathsToRemove != null)
+ {
+ int numRemove = pathsToRemove.size();
+ TreePath oldLead = leadPath;
+ if (numRemove == selection.length)
+ selection = null;
+ else
+ {
+ selection = new TreePath[selection.length - numRemove];
+ Iterator keep = selectedPaths.iterator();
+ for (int valid = 0; keep.hasNext(); valid++)
+ selection[valid] = (TreePath) keep.next();
+ }
+ // Update lead path.
+ if (leadPath != null && ! selectedPaths.contains(leadPath))
+ {
+ if (selection != null)
+ leadPath = selection[selection.length - 1];
+ else
leadPath = null;
}
- TreePath[] temp = new TreePath[selection.length - 1];
- System.arraycopy(selection, 0, temp, 0, index);
- System.arraycopy(selection, index + 1, temp, index,
- selection.length - index - 1);
- selection = new TreePath[temp.length];
- System.arraycopy(temp, 0, selection, 0, temp.length);
-
- fireValueChanged(new TreeSelectionEvent(this, v0, false,
- oldLead, leadPath));
+ else if (selection != null)
+ leadPath = selection[selection.length - 1];
+ else
+ leadPath = null;
+ updateLeadIndex();
+ resetRowSelection();
+ notifyPathChange(pathsToRemove, oldLead);
}
}
- insureRowContinuity();
}
}
@@ -572,19 +707,22 @@ public class DefaultTreeSelectionModel
*/
public void clearSelection()
{
- if (! isSelectionEmpty())
+ if (selection != null)
{
- TreeSelectionEvent event = new TreeSelectionEvent(
- this, selection, new boolean[selection.length], leadPath, null);
+ int selectionLength = selection.length;
+ boolean[] news = new boolean[selectionLength];
+ Arrays.fill(news, false);
+ TreeSelectionEvent event = new TreeSelectionEvent(this, selection,
+ news, leadPath,
+ null);
leadPath = null;
+ leadIndex = 0;
+ leadRow = 0;
+ selectedPaths.clear();
selection = null;
+ resetRowSelection();
fireValueChanged(event);
}
- else
- {
- leadPath = null;
- selection = null;
- }
}
/**
@@ -638,7 +776,7 @@ public class DefaultTreeSelectionModel
* @return an array of listeners
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
@@ -650,10 +788,43 @@ public class DefaultTreeSelectionModel
*/
public int[] getSelectionRows()
{
- if (rowMapper == null)
- return null;
- else
- return rowMapper.getRowsForPaths(selection);
+ int[] rows = null;
+ if (rowMapper != null && selection != null)
+ {
+ rows = rowMapper.getRowsForPaths(selection);
+ if (rows != null)
+ {
+ // Find invisible rows.
+ int invisible = 0;
+ for (int i = rows.length - 1; i >= 0; i--)
+ {
+ if (rows[i] == -1)
+ invisible++;
+
+ }
+ // Clean up invisible rows.
+ if (invisible > 0)
+ {
+ if (invisible == rows.length)
+ rows = null;
+ else
+ {
+ int[] newRows = new int[rows.length - invisible];
+ int visCount = 0;
+ for (int i = rows.length - 1; i >= 0; i--)
+ {
+ if (rows[i] != -1)
+ {
+ newRows[visCount] = rows[i];
+ visCount++;
+ }
+ }
+ rows = newRows;
+ }
+ }
+ }
+ }
+ return rows;
}
/**
@@ -663,16 +834,7 @@ public class DefaultTreeSelectionModel
*/
public int getMinSelectionRow()
{
- if ((rowMapper == null) || (selection == null) || (selection.length == 0))
- return - 1;
- else
- {
- int[] rows = rowMapper.getRowsForPaths(selection);
- int minRow = Integer.MAX_VALUE;
- for (int index = 0; index < rows.length; index++)
- minRow = Math.min(minRow, rows[index]);
- return minRow;
- }
+ return listSelectionModel.getMinSelectionIndex();
}
/**
@@ -682,16 +844,7 @@ public class DefaultTreeSelectionModel
*/
public int getMaxSelectionRow()
{
- if ((rowMapper == null) || (selection == null) || (selection.length == 0))
- return - 1;
- else
- {
- int[] rows = rowMapper.getRowsForPaths(selection);
- int maxRow = - 1;
- for (int index = 0; index < rows.length; index++)
- maxRow = Math.max(maxRow, rows[index]);
- return maxRow;
- }
+ return listSelectionModel.getMaxSelectionIndex();
}
/**
@@ -706,29 +859,7 @@ public class DefaultTreeSelectionModel
*/
public boolean isRowSelected(int row)
{
- // Return false if nothing is selected.
- if (isSelectionEmpty())
- return false;
-
- RowMapper mapper = getRowMapper();
-
- if (mapper instanceof AbstractLayoutCache)
- {
- // The absolute majority of cases, unless the TreeUI is very
- // seriously rewritten
- AbstractLayoutCache ama = (AbstractLayoutCache) mapper;
- TreePath path = ama.getPathForRow(row);
- return isPathSelected(path);
- }
- else
- {
- // Generic non optimized implementation.
- int[] rows = mapper.getRowsForPaths(selection);
- for (int i = 0; i < rows.length; i++)
- if (rows[i] == row)
- return true;
- return false;
- }
+ return listSelectionModel.isSelectedIndex(row);
}
/**
@@ -736,7 +867,32 @@ public class DefaultTreeSelectionModel
*/
public void resetRowSelection()
{
- // Nothing to do here.
+ listSelectionModel.clearSelection();
+ if (selection != null && rowMapper != null)
+ {
+ int[] rows = rowMapper.getRowsForPaths(selection);
+ // Update list selection model.
+ for (int i = 0; i < rows.length; i++)
+ {
+ int row = rows[i];
+ if (row != -1)
+ listSelectionModel.addSelectionInterval(row, row);
+ }
+ // Update lead selection.
+ if (leadIndex != -1 && rows != null)
+ leadRow = rows[leadIndex];
+ else if (leadPath != null)
+ {
+ TreePath[] tmp = new TreePath[]{ leadPath };
+ rows = rowMapper.getRowsForPaths(tmp);
+ leadRow = rows != null ? rows[0] : -1;
+ }
+ else
+ leadRow = -1;
+ insureRowContinuity();
+ }
+ else
+ leadRow = -1;
}
/**
@@ -766,6 +922,8 @@ public class DefaultTreeSelectionModel
*/
public void addPropertyChangeListener(PropertyChangeListener listener)
{
+ if (changeSupport == null)
+ changeSupport = new SwingPropertyChangeSupport(this);
changeSupport.addPropertyChangeListener(listener);
}
@@ -776,7 +934,8 @@ public class DefaultTreeSelectionModel
*/
public void removePropertyChangeListener(PropertyChangeListener listener)
{
- changeSupport.removePropertyChangeListener(listener);
+ if (changeSupport != null)
+ changeSupport.removePropertyChangeListener(listener);
}
/**
@@ -787,7 +946,12 @@ public class DefaultTreeSelectionModel
*/
public PropertyChangeListener[] getPropertyChangeListeners()
{
- return changeSupport.getPropertyChangeListeners();
+ PropertyChangeListener[] listeners = null;
+ if (changeSupport != null)
+ listeners = changeSupport.getPropertyChangeListeners();
+ else
+ listeners = new PropertyChangeListener[0];
+ return listeners;
}
/**
@@ -801,67 +965,41 @@ public class DefaultTreeSelectionModel
*/
protected void insureRowContinuity()
{
- if (selection == null || selection.length < 2)
- return;
- else if (selectionMode == CONTIGUOUS_TREE_SELECTION)
+ if (selectionMode == CONTIGUOUS_TREE_SELECTION && selection != null
+ && rowMapper != null)
{
- if (rowMapper == null)
- // This is the best we can do without the row mapper:
- selectOne();
- else
+ int min = listSelectionModel.getMinSelectionIndex();
+ if (min != -1)
{
- int[] rows = rowMapper.getRowsForPaths(selection);
- Arrays.sort(rows);
- int i;
- for (i = 1; i < rows.length; i++)
- {
- if (rows[i - 1] != rows[i] - 1)
- // Break if no longer continuous.
- break;
- }
-
- if (i < rows.length)
+ int max = listSelectionModel.getMaxSelectionIndex();
+ for (int i = min; i <= max; i++)
{
- TreePath[] ns = new TreePath[i];
- for (int j = 0; j < ns.length; j++)
- ns[i] = getPath(j);
- setSelectionPaths(ns);
+ if (! listSelectionModel.isSelectedIndex(i))
+ {
+ if (i == min)
+ clearSelection();
+ else
+ {
+ TreePath[] newSelection = new TreePath[i - min];
+ int[] rows = rowMapper.getRowsForPaths(selection);
+ for (int j = 0; j < rows.length; j++)
+ {
+ if (rows[j] < i)
+ newSelection[rows[j] - min] = selection[j];
+ }
+ setSelectionPaths(newSelection);
+ break;
+ }
+ }
}
}
}
- else if (selectionMode == SINGLE_TREE_SELECTION)
- selectOne();
+ else if (selectionMode == SINGLE_TREE_SELECTION && selection != null
+ && selection.length > 1)
+ setSelectionPath(selection[0]);
}
/**
- * Keep only one (normally last or leading) path in the selection.
- */
- private void selectOne()
- {
- if (leadIndex > 0 && leadIndex < selection.length)
- setSelectionPath(selection[leadIndex]);
- else
- setSelectionPath(selection[selection.length - 1]);
- }
-
- /**
- * Get path for the given row that must be in the current selection.
- */
- private TreePath getPath(int row)
- {
- if (rowMapper instanceof AbstractLayoutCache)
- return ((AbstractLayoutCache) rowMapper).getPathForRow(row);
- else
- {
- int[] rows = rowMapper.getRowsForPaths(selection);
- for (int i = 0; i < rows.length; i++)
- if (rows[i] == row)
- return selection[i];
- }
- throw new InternalError(row + " not in selection");
- }
-
- /**
* Returns <code>true</code> if the paths are contiguous (take subsequent
* rows in the diplayed tree view. The method returns <code>true</code> if
* we have no RowMapper assigned.
@@ -875,16 +1013,36 @@ public class DefaultTreeSelectionModel
if (rowMapper == null || paths.length < 2)
return true;
- int[] rows = rowMapper.getRowsForPaths(paths);
-
- // The patches may not be sorted.
- Arrays.sort(rows);
-
- for (int i = 1; i < rows.length; i++)
+ int length = paths.length;
+ TreePath[] tmp = new TreePath[1];
+ tmp[0] = paths[0];
+ int min = rowMapper.getRowsForPaths(tmp)[0];
+ BitSet selected = new BitSet();
+ int valid = 0;
+ for (int i = 0; i < length; i++)
{
- if (rows[i - 1] != rows[i] - 1)
- return false;
+ if (paths[i] != null)
+ {
+ tmp[0] = paths[i];
+ int[] rows = rowMapper.getRowsForPaths(tmp);
+ if (rows == null)
+ return false; // No row mapping yet, can't be selected.
+ int row = rows[0];
+ if (row == -1 || row < (min - length) || row > (min + length))
+ return false; // Not contiguous.
+ min = Math.min(min, row);
+ if (! selected.get(row))
+ {
+ selected.set(row);
+ valid++;
+ }
+
+ }
}
+ int max = valid + min;
+ for (int i = min; i < max; i++)
+ if (! selected.get(i))
+ return false; // Not contiguous.
return true;
}
@@ -904,34 +1062,51 @@ public class DefaultTreeSelectionModel
*/
protected boolean canPathsBeAdded(TreePath[] paths)
{
- if (rowMapper == null || isSelectionEmpty()
- || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
+ if (paths == null || paths.length == 0 || rowMapper == null
+ || selection == null || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
return true;
-
- TreePath [] all = new TreePath[paths.length + selection.length];
- System.arraycopy(paths, 0, all, 0, paths.length);
- System.arraycopy(selection, 0, all, paths.length, selection.length);
- return arePathsContiguous(all);
+ BitSet selected = new BitSet();
+ int min = listSelectionModel.getMinSelectionIndex();
+ int max = listSelectionModel.getMaxSelectionIndex();
+ TreePath[] tmp = new TreePath[1];
+ if (min != -1)
+ {
+ // Set the bitmask of selected elements.
+ for (int i = min; i <= max; i++)
+ selected.set(i);
+ }
+ else
+ {
+ tmp[0] = paths[0];
+ min = rowMapper.getRowsForPaths(tmp)[0];
+ max = min;
+ }
+ // Mark new paths as selected.
+ for (int i = paths.length - 1; i >= 0; i--)
+ {
+ if (paths[i] != null)
+ {
+ tmp[0] = paths[i];
+ int[] rows = rowMapper.getRowsForPaths(tmp);
+ if (rows == null)
+ return false; // Now row mapping yet, can't be selected.
+ int row = rows[0];
+ if (row == -1)
+ return false; // Now row mapping yet, can't be selected.
+ min = Math.min(min, row);
+ max = Math.max(max, row);
+ selected.set(row);
+ }
+ }
+ // Now look if the new selection would be contiguous.
+ for (int i = min; i <= max; i++)
+ if (! selected.get(i))
+ return false;
+ return true;
}
/**
- * Checks if the single path can be added to selection.
- */
- private boolean canPathBeAdded(TreePath path)
- {
- if (rowMapper == null || isSelectionEmpty()
- || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
- return true;
-
- TreePath[] all = new TreePath[selection.length + 1];
- System.arraycopy(selection, 0, all, 0, selection.length);
- all[all.length - 1] = path;
-
- return arePathsContiguous(all);
- }
-
- /**
* Checks if the paths can be removed without breaking the continuity of the
* selection according to selectionMode.
*
@@ -966,20 +1141,23 @@ public class DefaultTreeSelectionModel
* method will call listeners if invoked, but it is not called from the
* implementation of this class.
*
- * @param vPathes the vector of the changed patches
+ * @param vPaths the vector of the changed patches
* @param oldLeadSelection the old selection index
*/
- protected void notifyPathChange(Vector vPathes, TreePath oldLeadSelection)
+ protected void notifyPathChange(Vector vPaths, TreePath oldLeadSelection)
{
- TreePath[] pathes = new TreePath[vPathes.size()];
- for (int i = 0; i < pathes.length; i++)
- pathes[i] = (TreePath) vPathes.get(i);
- boolean[] news = new boolean[pathes.length];
- for (int i = 0; i < news.length; i++)
- news[i] = isPathSelected(pathes[i]);
+ int numChangedPaths = vPaths.size();
+ boolean[] news = new boolean[numChangedPaths];
+ TreePath[] paths = new TreePath[numChangedPaths];
+ for (int i = 0; i < numChangedPaths; i++)
+ {
+ PathPlaceHolder p = (PathPlaceHolder) vPaths.get(i);
+ news[i] = p.isNew;
+ paths[i] = p.path;
+ }
- TreeSelectionEvent event = new TreeSelectionEvent(this, pathes, news,
+ TreeSelectionEvent event = new TreeSelectionEvent(this, paths, news,
oldLeadSelection,
leadPath);
fireValueChanged(event);
@@ -991,22 +1169,20 @@ public class DefaultTreeSelectionModel
*/
protected void updateLeadIndex()
{
- if (isSelectionEmpty())
+ leadIndex = -1;
+ if (leadPath != null)
{
- leadRow = leadIndex = - 1;
- }
- else
- {
- leadRow = getRow(leadPath);
- for (int i = 0; i < selection.length; i++)
+ leadRow = -1;
+ if (selection == null)
+ leadPath = null;
+ else
{
- if (selection[i].equals(leadPath))
+ for (int i = selection.length - 1; i >= 0 && leadIndex == -1; i--)
{
- leadIndex = i;
- break;
+ if (selection[i] == leadPath)
+ leadIndex = i;
}
}
- leadIndex = leadRow;
}
}
diff --git a/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
index a699a6c9f21..dff9298e8f5 100644
--- a/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
+++ b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
@@ -480,7 +480,7 @@ public class FixedHeightLayoutCache
* @param parentPath the parent path
* @return the enumeration over pathes
*/
- public Enumeration getVisiblePathsFrom(TreePath parentPath)
+ public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath)
{
if (dirty)
update();
diff --git a/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java
index 0a787f7ca8c..8c70c13afd2 100644
--- a/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java
+++ b/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java
@@ -40,6 +40,7 @@ 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;
@@ -60,8 +61,11 @@ import javax.swing.event.TreeModelEvent;
* @author Audrius Meskauskas
*/
public class VariableHeightLayoutCache
- extends AbstractLayoutCache
+ extends AbstractLayoutCache
{
+
+ private static final Rectangle RECT_CACHE = new Rectangle();
+
/**
* The cached node record.
*/
@@ -73,8 +77,8 @@ public class VariableHeightLayoutCache
depth = aDepth;
parent = aParent;
node = aNode;
-
- isExpanded = expanded.contains(aNode);
+ isExpanded = expanded.contains(aNode);
+ bounds = new Rectangle(0, -1, 0, 0);
}
/**
@@ -102,7 +106,7 @@ public class VariableHeightLayoutCache
* Using this field saves one hashtable access operation.
*/
final boolean isExpanded;
-
+
/**
* The cached bounds of the tree row.
*/
@@ -160,11 +164,6 @@ public class VariableHeightLayoutCache
*/
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;
}
}
@@ -182,7 +181,7 @@ public class VariableHeightLayoutCache
/**
* Maps row numbers to nodes.
*/
- Hashtable row2node = new Hashtable();
+ ArrayList row2node = new ArrayList();
/**
* If true, the row map must be recomputed before using.
@@ -236,45 +235,54 @@ public class VariableHeightLayoutCache
return;
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);
- }
- }
+ countRows(root, null, 0, 0);
dirty = false;
}
/**
* Recursively counts all rows in the tree.
*/
- private final void countRows(Object node, Object parent, int depth)
+ private final int countRows(Object node, Object parent, int depth, int y)
{
- Integer n = new Integer(row2node.size());
- row2node.put(n, node);
-
- NodeRecord nr = new NodeRecord(n.intValue(), depth, node, parent);
+ 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);
-
- // For expanded nodes
+
+ if (visible)
+ y += r.height;
+
+ int sc = treeModel.getChildCount(node);
+ int deeper = depth + 1;
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);
+ y = countRows(child, node, deeper, y);
}
}
+ return y;
}
/**
@@ -309,10 +317,14 @@ public class VariableHeightLayoutCache
public void setExpandedState(TreePath path, boolean isExpanded)
{
if (isExpanded)
- expanded.add(path.getLastPathComponent());
+ {
+ int length = path.getPathCount();
+ for (int i = 0; i < length; i++)
+ expanded.add(path.getPathComponent(i));
+ }
else
expanded.remove(path.getLastPathComponent());
-
+
dirty = true;
}
@@ -339,25 +351,21 @@ public class VariableHeightLayoutCache
return null;
if (dirty)
update();
+
Object last = path.getLastPathComponent();
+ Rectangle result = null;
NodeRecord r = (NodeRecord) nodes.get(last);
- if (r == null)
- // This node is not visible.
- {
- rect.x = rect.y = rect.width = rect.height = 0;
- }
- else
+ if (r != null)
{
- if (r.bounds == null)
- {
- Rectangle dim = getNodeDimensions(last, r.row, r.depth,
- r.isExpanded, rect);
- r.bounds = dim;
- }
-
- rect.setRect(r.bounds);
+ // 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 rect;
+ return result;
}
/**
@@ -370,14 +378,17 @@ public class VariableHeightLayoutCache
{
if (dirty)
update();
- Object last = row2node.get(new Integer(row));
- if (last == null)
- return null;
- else
+
+ TreePath path = null;
+ // Search row in the nodes map. TODO: This is inefficient, optimize this.
+ Enumeration nodesEnum = nodes.elements();
+ while (nodesEnum.hasMoreElements() && path == null)
{
- NodeRecord r = (NodeRecord) nodes.get(last);
- return r.getPath();
+ NodeRecord record = (NodeRecord) nodesEnum.nextElement();
+ if (record.row == row)
+ path = record.getPath();
}
+ return path;
}
/**
@@ -390,7 +401,9 @@ public class VariableHeightLayoutCache
{
if (path == null)
return -1;
- if (dirty) update();
+
+ if (dirty)
+ update();
NodeRecord r = (NodeRecord) nodes.get(path.getLastPathComponent());
if (r == null)
@@ -451,8 +464,8 @@ public class VariableHeightLayoutCache
{
if (y < r.y)
return r.y - y;
- else if (y > r.y + r.height)
- return y - (r.y + r.height);
+ else if (y > r.y + r.height - 1)
+ return y - (r.y + r.height - 1);
else
return 0;
}
@@ -468,7 +481,7 @@ public class VariableHeightLayoutCache
*/
public int getVisibleChildCount(TreePath path)
{
- if (isExpanded(path))
+ if (! isExpanded(path) || treeModel == null)
return 0;
else
return treeModel.getChildCount(path.getLastPathComponent());
@@ -481,7 +494,7 @@ public class VariableHeightLayoutCache
* @param parentPath the parent path
* @return the enumeration over pathes
*/
- public Enumeration getVisiblePathsFrom(TreePath parentPath)
+ public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath)
{
if (dirty)
update();
@@ -493,7 +506,7 @@ public class VariableHeightLayoutCache
{
node = parentPath.getPathComponent(i);
nr = (NodeRecord) nodes.get(node);
- if (nr.row >= 0)
+ if (nr != null && nr.row >= 0)
p.add(node);
}
return p.elements();
@@ -558,15 +571,11 @@ public class VariableHeightLayoutCache
public void setModel(TreeModel newModel)
{
treeModel = newModel;
- // We need to clear the table and update the layout,
- // so that we don't end up with wrong data in the tables.
- expanded.clear();
- update();
+ dirty = true;
if (treeModel != null)
{
// The root node is expanded by default.
expanded.add(treeModel.getRoot());
- dirty = true;
}
}
@@ -590,15 +599,14 @@ public class VariableHeightLayoutCache
{
if (dirty)
update();
- totalHeight = 0;
- Enumeration en = nodes.elements();
- while (en.hasMoreElements())
+ int height = 0;
+ int rowCount = getRowCount();
+ if (rowCount > 0)
{
- NodeRecord nr = (NodeRecord) en.nextElement();
- Rectangle r = nr.getBounds();
- totalHeight += r.height;
+ NodeRecord last = (NodeRecord) nodes.get(row2node.get(rowCount - 1));
+ height = last.bounds.y + last.bounds.height;
}
- return totalHeight;
+ return height;
}
/**
@@ -614,10 +622,36 @@ public class VariableHeightLayoutCache
while (en.hasMoreElements())
{
NodeRecord nr = (NodeRecord) en.nextElement();
- Rectangle r = nr.getBounds();
- if (r.x + r.width > maximalWidth)
- maximalWidth = r.x + r.width;
+ 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;
+ }
}
OpenPOWER on IntegriCloud