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);
   }

Reply via email to