I have essentially rewritten most of JLayeredPane. I noticed some troubles with this class and read a little in the OReilly Swing book 1st edition. I learned that we did a couple of things wrong in this class:
- we stored all components and their layers in the Hashtable. This is wrong. JComponents should have their layer stored as client property with LAYER_PROPERTY as key. - a lot of the methods used to throw IllegalArgumentExceptions for unusual arguments like components that are not children of the layered pane. These methods should ignore this instead and return -1 or 0. (This error actually caused several problems in some applications) Also, our implementation used a second datastructure called 'layers' to manage the layers. This is really not needed and makes maintainence of the code less straightforward. I removed this and rewrote most stuff in a more straightforward manner. 2006-01-27 Roman Kennke <[EMAIL PROTECTED]> * javax/swing/JLayeredPane.java (FRAME_CONTENT_LAYER): Made field final. (componentToLayer): Made field private. (rectCache): Removed field. (layers): Removed field. (JLayeredPane()): Removed initialization of removed fields. (getLayer): Rewritten to make use of client properties in JComponents and to be more straighforward. (static getLayer): Rewritten to make use of client properties in JComponents. (layerToRange): Removed method. (incrLayer): Removed method. (decrLayer): Removed method. (highestLayer): Rewritten to be more straightforward. (lowestLayer): Rewritten to be more straightforward. (getPosition): Rewritten to be more straightforward. (getComponentsInLayer): Rewritten to be more straightforward. (getComponentCountInLayer): Rewritten to be more straightforward. (getIndexOf): Rewritten to be more straightforward. (inserIndexForLayer): Rewritten to be more straightforward. (remove): Rewritten to be more straightforward. (setLayer): Rewritten to be more straightforward. (addImpl): Rewritten to be more straightforward. (putLayer): Rewritten to be more straightforward. /Roman
Index: javax/swing/JLayeredPane.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/JLayeredPane.java,v retrieving revision 1.37 diff -u -r1.37 JLayeredPane.java --- javax/swing/JLayeredPane.java 27 Jan 2006 15:51:21 -0000 1.37 +++ javax/swing/JLayeredPane.java 27 Jan 2006 19:56:19 -0000 @@ -43,11 +43,7 @@ import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; -import java.awt.Shape; import java.util.Hashtable; -import java.util.Iterator; -import java.util.Map; -import java.util.TreeMap; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; @@ -119,6 +115,7 @@ * component indexing and position order</p> * * @author Graydon Hoare ([EMAIL PROTECTED]) + * @author Roman Kennke ([EMAIL PROTECTED]) */ public class JLayeredPane extends JComponent implements Accessible { @@ -150,7 +147,7 @@ public static final String LAYER_PROPERTY = "layeredContainerLayer"; - public static Integer FRAME_CONTENT_LAYER = new Integer (-30000); + public static final Integer FRAME_CONTENT_LAYER = new Integer (-30000); public static final Integer DEFAULT_LAYER = new Integer (0); public static final Integer PALETTE_LAYER = new Integer (100); @@ -158,14 +155,10 @@ public static final Integer POPUP_LAYER = new Integer (300); public static final Integer DRAG_LAYER = new Integer (400); - TreeMap layers; // Layer Number (Integer) -> Layer Size (Integer) - Hashtable componentToLayer; // Component -> Layer Number (Integer) + private Hashtable componentToLayer; // Component -> Layer Number (Integer) - private transient Rectangle rectCache; - public JLayeredPane() { - layers = new TreeMap (); componentToLayer = new Hashtable (); setLayout(null); } @@ -173,47 +166,50 @@ /** * Looks up the layer a child component is currently assigned to. * + * If <code>c</code> is an instance of [EMAIL PROTECTED] JComponent}, then the layer + * is fetched from the client property with the key [EMAIL PROTECTED] #LAYER_PROPERTY}. + * Otherwise it is looked up in an internal hashtable that maps + * non-JComponent components to layers. If the components cannot be found + * in either way, the [EMAIL PROTECTED] #DEFAULT_LAYER} is returned. + * * @param c the component to look up. - * @return the layer the component is currently assigned to, in this container. - * @throws IllegalArgumentException if the component is not a child of this container. + * + * @return the layer the component is currently assigned to; if the component + * is not in this layered pane, then 0 (DEFAULT_LAYER) is returned */ public int getLayer(Component c) { - Component myComp = c; - while(! componentToLayer.containsKey(myComp)) + Integer layerObj; + if (c instanceof JComponent) { - myComp = myComp.getParent(); - if (myComp == null) - break; + JComponent jc = (JComponent) c; + layerObj = (Integer) jc.getClientProperty(LAYER_PROPERTY); } - if (myComp == null) - throw new IllegalArgumentException - ("component is not in this JLayeredPane"); - Integer layerObj = (Integer) componentToLayer.get(myComp); + else + layerObj = (Integer) componentToLayer.get(c); + + if (layerObj == null) + layerObj = DEFAULT_LAYER; + return layerObj.intValue(); } /** - * Looks up the layer of <code>comp</code> in the component's nearest - * JLayeredPane ancestor. If <code>comp</code> is not contained - * in a JLayeredPane, the value 0 (default layer) is returned. - * + * Looks up the layer in the client property with the key + * [EMAIL PROTECTED] #LAYER_PROPERTY} of <code>comp</code>. If no such property can be + * found, we return <code>0</code> ([EMAIL PROTECTED] #DEFAULT_LAYER}). + * * @param comp the component for which the layer is looked up * - * @return the layer of <code>comp</code> in its nearest JLayeredPane - * ancestor + * @return the layer of <code>comp</code> as stored in the corresponding + * client property, or <code>0</code> if there is no such property */ public static int getLayer(JComponent comp) { - JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass - (JLayeredPane.class, comp); - if (lp == null) - return 0; - else - // The cast here forces the call to the instance method getLayer() - // instead of the static method (this would lead to infinite - // recursion). - return lp.getLayer((Component) comp); + Integer layerObj = (Integer) comp.getClientProperty(LAYER_PROPERTY); + if (layerObj == null) + layerObj = DEFAULT_LAYER; + return layerObj.intValue(); } /** @@ -236,105 +232,49 @@ } /** - * <p>Returns a pair of ints representing a half-open interval - * <code>[top, bottom)</code>, which is the range of component indices - * the provided layer number corresponds to.</p> - * - * <p>Note that "bottom" is <em>not</em> included in the interval of - * component indices in this layer: a layer with 0 elements in it has - * <code>ret[0] == ret[1]</code>.</p> - * - * @param layer the layer to look up. - * @return the half-open range of indices this layer spans. - * @throws IllegalArgumentException if layer does not refer to an active layer - * in this container. - */ - private int[] layerToRange (Integer layer) - { - int[] ret = new int[2]; - ret[1] = getComponents ().length; - Iterator i = layers.entrySet ().iterator (); - while (i.hasNext()) - { - Map.Entry pair = (Map.Entry) i.next(); - Integer layerNum = (Integer) pair.getKey (); - Integer layerSz = (Integer) pair.getValue (); - int layerInt = layerNum.intValue(); - if (layerInt == layer.intValue()) - { - ret[0] = ret[1] - layerSz.intValue (); - break; - } - // In the following case there exists no layer with the specified - // number, so we return an empty interval here with the index at which - // such a layer would be inserted - else if (layerInt > layer.intValue()) - { - ret[1] = ret[0]; - break; - } - else - { - ret[1] -= layerSz.intValue (); - } - } - return ret; - } - - /** - * Increments the recorded size of a given layer. - * - * @param layer the layer number to increment. - * @see #incrLayer - */ - private void incrLayer(Integer layer) - { - int sz = 1; - if (layers.containsKey (layer)) - sz += ((Integer)(layers.get (layer))).intValue (); - layers.put (layer, new Integer(sz)); - } - - /** - * Decrements the recorded size of a given layer. - * - * @param layer the layer number to decrement. - * @see #incrLayer - */ - private void decrLayer(Integer layer) - { - int sz = 0; - if (layers.containsKey (layer)) - sz = ((Integer)(layers.get (layer))).intValue () - 1; - layers.put (layer, new Integer(sz)); - } - - /** * Return the greatest layer number currently in use, in this container. * This number may legally be positive <em>or</em> negative. * - * @return the least layer number. + * @return the highest layer number + * * @see #lowestLayer() */ public int highestLayer() { - if (layers.size() == 0) - return 0; - return ((Integer)(layers.lastKey ())).intValue (); + Component[] components = getComponents(); + int highest; + if (components.length == 0) + highest = 0; + else + { + highest = Integer.MIN_VALUE; + for (int i = 0; i < components.length; i++) + highest = Math.max(highest, getLayer(components[i])); + } + return highest; } /** * Return the least layer number currently in use, in this container. * This number may legally be positive <em>or</em> negative. * - * @return the least layer number. + * @return the least layer number + * * @see #highestLayer() */ public int lowestLayer() { - if (layers.size() == 0) - return 0; - return ((Integer)(layers.firstKey ())).intValue (); + Component[] components = getComponents(); + int lowest; + if (components.length == 0) + lowest = 0; + else + { + lowest = Integer.MAX_VALUE; + for (int i = 0; i < components.length; i++) + lowest = Math.max(lowest, getLayer(components[i])); + } + return lowest; } /** @@ -343,9 +283,8 @@ * layer, so is usually the component which occludes the most other * components in its layer. * - * @param c the component to move to the front of its layer. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * @param c the component to move to the front of its layer + * * @see #moveToBack */ public void moveToFront(Component c) @@ -363,8 +302,7 @@ * other components in its layer.</p> * * @param c the component to move to the back of its layer. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * * @see #moveToFront */ public void moveToBack(Component c) @@ -377,25 +315,30 @@ * from the "front" (position 0) to the "back" (position N-1), and drawn from * the back towards the front. * - * @param c the component to get the position of. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * @param c the component to get the position of + * + * @return the position of <code>c</code> within its layer or -1 if + * <code>c</code> is not a child of this layered pane + * * @see #setPosition */ public int getPosition(Component c) { - int layer = getLayer (c); - int[] range = layerToRange(new Integer(layer)); - int top = range[0]; - int bot = range[1]; - Component[] comps = getComponents (); - for (int i = top; i < bot; ++i) - { - if (comps[i] == c) - return i - top; - } - // should have found it - throw new IllegalArgumentException (); + int pos = -1; + int index = getIndexOf(c); + Component[] components = getComponents(); + int layer = getLayer(c); + if (index >= 0) + { + for (int i = index; i >= 0; --i) + { + if (layer == getLayer(components[i])) + pos++; + else + break; + } + } + return pos; } /** @@ -403,10 +346,9 @@ * from the "front" (position 0) to the "back" (position N-1), and drawn from * the back towards the front. * - * @param c the component to change the position of. - * @param position the position to assign the component to. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * @param c the component to change the position of + * @param position the position to assign the component to + * * @see #getPosition */ public void setPosition(Component c, int position) @@ -428,39 +370,44 @@ * container. Components are ordered front-to-back, with the "front" * element (which draws last) at position 0 of the returned array. * - * @param layer the layer to return components from. - * @return the components in the layer. + * @param layer the layer to return components from + * + * @return the components in the layer */ public Component[] getComponentsInLayer(int layer) { - int[] range = layerToRange (getObjectForLayer (layer)); - if (range[0] == range[1]) - return new Component[0]; - else - { - Component[] comps = getComponents (); - int sz = range[1] - range[0]; - Component[] nc = new Component[sz]; - for (int i = 0; i < sz; ++i) - nc[i] = comps[range[0] + i]; - return nc; - } + Component[] inLayer = new Component[getComponentCountInLayer(layer)]; + Component[] components = getComponents(); + int j = 0; + for (int i = 0; i < components.length; ++i) + { + if (layer == getLayer(components[i])) + { + inLayer[j] = components[i]; + j++; + } + } + return inLayer; } /** * Return the number of components within a layer of this * container. * - * @param layer the layer count components in. - * @return the number of components in the layer. + * @param layer the layer count components in + * + * @return the number of components in the layer */ public int getComponentCountInLayer(int layer) { - int[] range = layerToRange (getObjectForLayer (layer)); - if (range[0] == range[1]) - return 0; - else - return (range[1] - range[0]); + Component[] components = getComponents(); + int count = 0; + for (int i = components.length - 1; i >= 0; --i) + { + if (getLayer(components[i]) == layer) + count++; + } + return count; } /** @@ -479,23 +426,24 @@ * drawing order of all children of the container. * * @param c the component to look up. - * @return the external index of the component. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * + * @return the external index of the component or <code>-1</code> if + * <code>c</code> is not a child of this layered pane */ public int getIndexOf(Component c) { - int layer = getLayer (c); - int[] range = layerToRange(new Integer(layer)); - Component[] comps = getComponents(); - for (int i = range[0]; i < range[1]; ++i) - { - if (comps[i] == c) - return i; - } - // should have found the component during iteration - throw new IllegalArgumentException (); - } + int index = -1; + Component[] components = getComponents(); + for (int i = 0; i < components.length; ++i) + { + if (components[i] == c) + { + index = i; + break; + } + } + return index; + } /** * Return an Integer object which holds the same int value as the @@ -503,6 +451,7 @@ * identical Integer objects which we allocate. * * @param layer the layer number as an int. + * * @return the layer number as an Integer, possibly shared. */ protected Integer getObjectForLayer(int layer) @@ -541,25 +490,35 @@ * * @param layer the layer in which to insert a component. * @param position the position in the layer at which to insert a component. + * * @return the index at which to insert the component. */ protected int insertIndexForLayer(int layer, int position) { + Component[] components = getComponents(); + int index = components.length; - Integer lobj = getObjectForLayer (layer); - if (! layers.containsKey(lobj)) - layers.put (lobj, new Integer (0)); - int[] range = layerToRange (lobj); - if (range[0] == range[1]) - return range[0]; - - int top = range[0]; - int bot = range[1]; - - if (position == -1 || position > (bot - top)) - return bot; - else - return top + position; + // Try to find the start index of the specified layer. + int p = -1; + for (int i = components.length - 1; i >= 0; --i) + { + int l = getLayer(components[i]); + if (l > layer) + index = i; + // If we are in the layer we look for, try to find the position. + else if (l == layer) + { + p++; + if (p >= position) + index = i; + else + break; + } + // No need to look further if the layer at i is smaller than layer. + else + break; + } + return index; } /** @@ -571,12 +530,9 @@ public void remove(int index) { Component c = getComponent(index); - int layer = getLayer(c); - decrLayer(new Integer(layer)); - componentToLayer.remove(c); + if (! (c instanceof JComponent)) + componentToLayer.remove(c); super.remove(index); - // FIXME: Figure out if this call is correct. - revalidate(); } /** @@ -592,7 +548,7 @@ */ public void setLayer(Component c, int layer) { - componentToLayer.put (c, getObjectForLayer (layer)); + setLayer(c, layer, -1); } /** @@ -602,15 +558,20 @@ * @param layer the layer number to assign to the component. * @param position the position number to assign to the component. */ - public void setLayer(Component c, - int layer, - int position) - { - remove(c); - add(c, getObjectForLayer (layer)); - setPosition(c, position); - revalidate(); - repaint(); + public void setLayer(Component c, int layer, int position) + { + Integer layerObj = getObjectForLayer(layer); + if (c instanceof JComponent) + { + JComponent jc = (JComponent) c; + jc.putClientProperty(LAYER_PROPERTY, layerObj); + } + else + componentToLayer.put (c, layerObj); + + // Set position only of component is already added to this layered pane. + if (getIndexOf(c) != -1) + setPosition(c, position); } /** @@ -625,19 +586,14 @@ */ protected void addImpl(Component comp, Object layerConstraint, int index) { - Integer layer; + int layer; if (layerConstraint != null && layerConstraint instanceof Integer) - layer = (Integer) layerConstraint; - else if (componentToLayer.containsKey (comp)) - layer = (Integer) componentToLayer.remove (comp); + layer = ((Integer) layerConstraint).intValue(); else - layer = DEFAULT_LAYER; - - int newIdx = insertIndexForLayer(layer.intValue (), index); + layer = getLayer(comp); - componentToLayer.put (comp, layer); - incrLayer (layer); - + int newIdx = insertIndexForLayer(layer, index); + setLayer(comp, layer); super.addImpl(comp, null, newIdx); } @@ -649,7 +605,7 @@ */ public static void putLayer(JComponent component, int layer) { - getLayeredPaneAbove(component).setLayer(component, layer); + component.putClientProperty(LAYER_PROPERTY, new Integer(layer)); } /** @@ -743,7 +699,7 @@ remove(p2 + layerOffs); remove(p1 + layerOffs); // add() wants the position within the layer. - Integer l = new Integer(layer); + Integer l = getObjectForLayer(layer); add(c2, l, p1); add(c1, l, p2); }