1 /* JLayeredPane.java --
2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java
.awt
.Color
;
42 import java
.awt
.Component
;
43 import java
.awt
.Container
;
44 import java
.awt
.Graphics
;
45 import java
.awt
.Rectangle
;
46 import java
.awt
.Shape
;
47 import java
.util
.Hashtable
;
48 import java
.util
.Iterator
;
50 import java
.util
.TreeMap
;
52 import javax
.accessibility
.Accessible
;
53 import javax
.accessibility
.AccessibleContext
;
54 import javax
.accessibility
.AccessibleRole
;
57 * A container that adds depth to the usual <code>Container</code> semantics.
58 * Each child component of a <code>Layered Pane</code> is placed within one
59 * of several layers. <code>JLayeredPane</code> defines a set of standard
60 * layers. The pre-defined sets are (in the order from button to top):
63 * <dt>{@link #DEFAULT_LAYER}</dt>
64 * <dd>The layer where most of the normal components are placed. This
65 * is the bottommost layer.</dd>
67 * <dt>{@link #PALETTE_LAYER}</dt>
68 * <dd>Palette windows are placed in this layer.</dd>
70 * <dt>{@link #MODAL_LAYER}</dt>
71 * <dd>The layer where internal modal dialog windows are placed.</dd>
73 * <dt>{@link #POPUP_LAYER}</dt>
74 * <dd>The layer for popup menus</dd>
76 * <dt>{@link #DRAG_LAYER}</dt>
77 * <dd>Components that are beeing dragged are temporarily placed in
81 * <p>A child is in exactly one of these layers at any time, though there may
82 * be other layers if someone creates them.</p>
84 * <p>You can add a component to a specific layer using the
85 * {@link Container#add(Component, Object)} method. I.e.
86 * <code>layeredPane.add(comp, JLayeredPane.MODAL_LAYER)</code> will add the
87 * component <code>comp</code> to the modal layer of <code>layeredPane</code>.
90 * <p>To change the layer of a component that is already a child of
91 * a <code>JLayeredPane</code>, use the {@link #setLayer(Component, int)}
94 * <p>The purpose of this class is to translate this view of "layers" into a
95 * contiguous array of components: the one held in our ancestor,
96 * {@link java.awt.Container}.</p>
98 * <p>There is a precise set of words we will use to refer to numbers within
102 * <dt>Component Index:</dt>
103 * <dd>An offset into the <code>component</code> array held in our ancestor,
104 * {@link java.awt.Container}, from <code>[0 .. component.length)</code>. The drawing
105 * rule with indices is that 0 is drawn last.</dd>
107 * <dt>Layer Number:</dt>
108 * <dd>A general <code>int</code> specifying a layer within this component. Negative
109 * numbers are drawn first, then layer 0, then positive numbered layers, in
110 * ascending order.</dd>
113 * <dd>An offset into a layer's "logical drawing order". Layer position 0
114 * is drawn last. Layer position -1 is a synonym for the first layer
115 * position (the logical "bottom").</dd>
118 * <p><b>Note:</b> the layer numbering order is the <em>reverse</em> of the
119 * component indexing and position order</p>
121 * @author Graydon Hoare (graydon@redhat.com)
123 public class JLayeredPane
extends JComponent
implements Accessible
127 * Provides accessibility support for <code>JLayeredPane</code>.
129 protected class AccessibleJLayeredPane
extends AccessibleJComponent
132 * Creates a new instance of <code>AccessibleJLayeredPane</code>.
134 public AccessibleJLayeredPane()
136 // Nothing to do here.
140 * Returns the accessble role of <code>JLayeredPane</code>,
141 * {@link AccessibleRole#LAYERED_PANE}.
143 public AccessibleRole
getAccessibleRole()
145 return AccessibleRole
.LAYERED_PANE
;
149 private static final long serialVersionUID
= 5534920399324590459L;
151 public static final String LAYER_PROPERTY
= "layeredContainerLayer";
153 public static Integer FRAME_CONTENT_LAYER
= new Integer (-30000);
155 public static Integer DEFAULT_LAYER
= new Integer (0);
156 public static Integer PALETTE_LAYER
= new Integer (100);
157 public static Integer MODAL_LAYER
= new Integer (200);
158 public static Integer POPUP_LAYER
= new Integer (300);
159 public static Integer DRAG_LAYER
= new Integer (400);
161 TreeMap layers
; // Layer Number (Integer) -> Layer Size (Integer)
162 Hashtable componentToLayer
; // Component -> Layer Number (Integer)
164 private transient Rectangle rectCache
;
166 public JLayeredPane()
168 layers
= new TreeMap ();
169 componentToLayer
= new Hashtable ();
174 * Looks up the layer a child component is currently assigned to.
176 * @param c the component to look up.
177 * @return the layer the component is currently assigned to, in this container.
178 * @throws IllegalArgumentException if the component is not a child of this container.
180 public int getLayer(Component c
)
182 Component myComp
= c
;
183 while(! componentToLayer
.containsKey(myComp
))
185 myComp
= myComp
.getParent();
190 throw new IllegalArgumentException
191 ("component is not in this JLayeredPane");
192 Integer layerObj
= (Integer
) componentToLayer
.get(myComp
);
193 return layerObj
.intValue();
197 * Looks up the layer of <code>comp</code> in the component's nearest
198 * JLayeredPane ancestor. If <code>comp</code> is not contained
199 * in a JLayeredPane, the value 0 (default layer) is returned.
201 * @param comp the component for which the layer is looked up
203 * @return the layer of <code>comp</code> in its nearest JLayeredPane
206 public static int getLayer(JComponent comp
)
208 JLayeredPane lp
= (JLayeredPane
) SwingUtilities
.getAncestorOfClass
209 (JLayeredPane
.class, comp
);
213 // The cast here forces the call to the instance method getLayer()
214 // instead of the static method (this would lead to infinite
216 return lp
.getLayer((Component
) comp
);
220 * Returns the first JLayeredPane that contains the Component
221 * <code>comp</code> or <code>null</code> if <code>comp</code> is
222 * not contained in a JLayeredPane.
224 * @param comp the component for which we are searching the JLayeredPane
227 * @return the first JLayeredPane that contains the Component
228 * <code>comp</code> or <code>null</code> if <code>comp</code> is
229 * not contained in a JLayeredPane
231 public static JLayeredPane
getLayeredPaneAbove(Component comp
)
233 JLayeredPane lp
= (JLayeredPane
) SwingUtilities
.getAncestorOfClass
234 (JLayeredPane
.class, comp
);
239 * <p>Returns a pair of ints representing a half-open interval
240 * <code>[top, bottom)</code>, which is the range of component indices
241 * the provided layer number corresponds to.</p>
243 * <p>Note that "bottom" is <em>not</em> included in the interval of
244 * component indices in this layer: a layer with 0 elements in it has
245 * <code>ret[0] == ret[1]</code>.</p>
247 * @param layer the layer to look up.
248 * @return the half-open range of indices this layer spans.
249 * @throws IllegalArgumentException if layer does not refer to an active layer
252 private int[] layerToRange (Integer layer
)
254 int[] ret
= new int[2];
255 ret
[1] = getComponents ().length
;
256 Iterator i
= layers
.entrySet ().iterator ();
259 Map
.Entry pair
= (Map
.Entry
) i
.next();
260 Integer layerNum
= (Integer
) pair
.getKey ();
261 Integer layerSz
= (Integer
) pair
.getValue ();
262 int layerInt
= layerNum
.intValue();
263 if (layerInt
== layer
.intValue())
265 ret
[0] = ret
[1] - layerSz
.intValue ();
268 // In the following case there exists no layer with the specified
269 // number, so we return an empty interval here with the index at which
270 // such a layer would be inserted
271 else if (layerInt
> layer
.intValue())
278 ret
[1] -= layerSz
.intValue ();
285 * Increments the recorded size of a given layer.
287 * @param layer the layer number to increment.
290 private void incrLayer(Integer layer
)
293 if (layers
.containsKey (layer
))
294 sz
+= ((Integer
)(layers
.get (layer
))).intValue ();
295 layers
.put (layer
, new Integer(sz
));
299 * Decrements the recorded size of a given layer.
301 * @param layer the layer number to decrement.
304 private void decrLayer(Integer layer
)
307 if (layers
.containsKey (layer
))
308 sz
= ((Integer
)(layers
.get (layer
))).intValue () - 1;
309 layers
.put (layer
, new Integer(sz
));
313 * Return the greatest layer number currently in use, in this container.
314 * This number may legally be positive <em>or</em> negative.
316 * @return the least layer number.
317 * @see #lowestLayer()
319 public int highestLayer()
321 if (layers
.size() == 0)
323 return ((Integer
)(layers
.lastKey ())).intValue ();
327 * Return the least layer number currently in use, in this container.
328 * This number may legally be positive <em>or</em> negative.
330 * @return the least layer number.
331 * @see #highestLayer()
333 public int lowestLayer()
335 if (layers
.size() == 0)
337 return ((Integer
)(layers
.firstKey ())).intValue ();
341 * Moves a component to the "front" of its layer. The "front" is a
342 * synonym for position 0, which is also the last position drawn in each
343 * layer, so is usually the component which occludes the most other
344 * components in its layer.
346 * @param c the component to move to the front of its layer.
347 * @throws IllegalArgumentException if the component is not a child of
351 public void moveToFront(Component c
)
357 * <p>Moves a component to the "back" of its layer. The "back" is a
358 * synonym for position N-1 (also known as position -1), where N is the
359 * size of the layer.</p>
361 * <p>The "back" of a layer is the first position drawn, so the component at
362 * the "back" is usually the component which is occluded by the most
363 * other components in its layer.</p>
365 * @param c the component to move to the back of its layer.
366 * @throws IllegalArgumentException if the component is not a child of
370 public void moveToBack(Component c
)
376 * Return the position of a component within its layer. Positions are assigned
377 * from the "front" (position 0) to the "back" (position N-1), and drawn from
378 * the back towards the front.
380 * @param c the component to get the position of.
381 * @throws IllegalArgumentException if the component is not a child of
385 public int getPosition(Component c
)
387 int layer
= getLayer (c
);
388 int[] range
= layerToRange(new Integer(layer
));
391 Component
[] comps
= getComponents ();
392 for (int i
= top
; i
< bot
; ++i
)
397 // should have found it
398 throw new IllegalArgumentException ();
402 * Change the position of a component within its layer. Positions are assigned
403 * from the "front" (position 0) to the "back" (position N-1), and drawn from
404 * the back towards the front.
406 * @param c the component to change the position of.
407 * @param position the position to assign the component to.
408 * @throws IllegalArgumentException if the component is not a child of
412 public void setPosition(Component c
, int position
)
414 int layer
= getLayer (c
);
415 int[] range
= layerToRange(new Integer(layer
));
416 if (range
[0] == range
[1])
417 throw new IllegalArgumentException ();
422 position
= (bot
- top
) - 1;
423 int targ
= Math
.min(top
+ position
, bot
-1);
426 Component
[] comps
= getComponents();
427 for (int i
= top
; i
< bot
; ++i
)
436 // should have found it
437 throw new IllegalArgumentException();
440 super.swapComponents(curr
, targ
);
443 super.swapComponents (curr
, --curr
);
450 * Return an array of all components within a layer of this
451 * container. Components are ordered front-to-back, with the "front"
452 * element (which draws last) at position 0 of the returned array.
454 * @param layer the layer to return components from.
455 * @return the components in the layer.
457 public Component
[] getComponentsInLayer(int layer
)
459 int[] range
= layerToRange (getObjectForLayer (layer
));
460 if (range
[0] == range
[1])
461 return new Component
[0];
464 Component
[] comps
= getComponents ();
465 int sz
= range
[1] - range
[0];
466 Component
[] nc
= new Component
[sz
];
467 for (int i
= 0; i
< sz
; ++i
)
468 nc
[i
] = comps
[range
[0] + i
];
474 * Return the number of components within a layer of this
477 * @param layer the layer count components in.
478 * @return the number of components in the layer.
480 public int getComponentCountInLayer(int layer
)
482 int[] range
= layerToRange (getObjectForLayer (layer
));
483 if (range
[0] == range
[1])
486 return (range
[1] - range
[0]);
490 * Return a hashtable mapping child components of this container to
491 * Integer objects representing the component's layer assignments.
493 protected Hashtable
getComponentToLayer()
495 return componentToLayer
;
499 * Return the index of a component within the underlying (contiguous)
500 * array of children. This is a "raw" number which does not represent the
501 * child's position in a layer, but rather its position in the logical
502 * drawing order of all children of the container.
504 * @param c the component to look up.
505 * @return the external index of the component.
506 * @throws IllegalArgumentException if the component is not a child of
509 public int getIndexOf(Component c
)
511 int layer
= getLayer (c
);
512 int[] range
= layerToRange(new Integer(layer
));
513 Component
[] comps
= getComponents();
514 for (int i
= range
[0]; i
< range
[1]; ++i
)
519 // should have found the component during iteration
520 throw new IllegalArgumentException ();
524 * Return an Integer object which holds the same int value as the
525 * parameter. This is strictly an optimization to minimize the number of
526 * identical Integer objects which we allocate.
528 * @param layer the layer number as an int.
529 * @return the layer number as an Integer, possibly shared.
531 protected Integer
getObjectForLayer(int layer
)
536 return FRAME_CONTENT_LAYER
;
539 return DEFAULT_LAYER
;
542 return PALETTE_LAYER
;
557 return new Integer(layer
);
561 * Computes an index at which to request the superclass {@link
562 * java.awt.Container} inserts a component, given an abstract layer and
565 * @param layer the layer in which to insert a component.
566 * @param position the position in the layer at which to insert a component.
567 * @return the index at which to insert the component.
569 protected int insertIndexForLayer(int layer
, int position
)
572 Integer lobj
= getObjectForLayer (layer
);
573 if (! layers
.containsKey(lobj
))
574 layers
.put (lobj
, new Integer (0));
575 int[] range
= layerToRange (lobj
);
576 if (range
[0] == range
[1])
582 if (position
== -1 || position
> (bot
- top
))
585 return top
+ position
;
589 * Removes a child from this container. The child is specified by
590 * index. After removal, the child no longer occupies a layer.
592 * @param index the index of the child component to remove.
594 public void remove(int index
)
596 Component c
= getComponent(index
);
597 int layer
= getLayer(c
);
598 decrLayer(new Integer(layer
));
599 componentToLayer
.remove(c
);
601 // FIXME: Figure out if this call is correct.
606 * <p>Set the layer property for a component, within this container. The
607 * component will be implicitly mapped to the bottom-most position in the
608 * layer, but only if added <em>after</em> calling this method.</p>
610 * <p>Read that carefully: this method should be called <em>before</em> the
611 * component is added to the container.</p>
613 * @param c the component to set the layer property for.
614 * @param layer the layer number to assign to the component.
616 public void setLayer(Component c
, int layer
)
618 componentToLayer
.put (c
, getObjectForLayer (layer
));
622 * Set the layer and position of a component, within this container.
624 * @param c the child component to set the layer property for.
625 * @param layer the layer number to assign to the component.
626 * @param position the position number to assign to the component.
628 public void setLayer(Component c
,
633 add(c
, getObjectForLayer (layer
));
634 setPosition(c
, position
);
640 * Overrides the default implementation from {@link java.awt.Container}
641 * such that <code>layerConstraint</code> is interpreted as an {@link
642 * Integer}, specifying the layer to which the component will be added
643 * (at the bottom position).
645 * @param comp the component to add.
646 * @param layerConstraint an integer specifying the layer to add the component to.
647 * @param index an ignored parameter, for compatibility.
649 protected void addImpl(Component comp
, Object layerConstraint
, int index
)
652 if (layerConstraint
!= null && layerConstraint
instanceof Integer
)
653 layer
= (Integer
) layerConstraint
;
654 else if (componentToLayer
.containsKey (comp
))
655 layer
= (Integer
) componentToLayer
.remove (comp
);
657 layer
= DEFAULT_LAYER
;
659 int newIdx
= insertIndexForLayer(layer
.intValue (), index
);
661 componentToLayer
.put (comp
, layer
);
664 super.addImpl(comp
, null, newIdx
);
668 * Sets the layer property for a JComponent.
670 * @param component the component for which to set the layer
671 * @param layer the layer property to set
673 public static void putLayer(JComponent component
, int layer
)
675 getLayeredPaneAbove(component
).setLayer(component
, layer
);
679 * Returns the accessible context for this <code>JLayeredPane</code>.
681 * @return the accessible context for this <code>JLayeredPane</code>
683 public AccessibleContext
getAccessibleContext()
685 if (accessibleContext
== null)
686 accessibleContext
= new AccessibleJLayeredPane();
687 return accessibleContext
;
691 * This method is overridden order to provide a reasonable painting
692 * mechanism for <code>JLayeredPane</code>. This is necessary since
693 * <code>JLayeredPane</code>'s do not have an own UI delegate.
695 * Basically this method clears the background for the
696 * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>.
698 * @param g the graphics context to use
700 public void paint(Graphics g
)
704 Color oldColor
= g
.getColor();
705 Rectangle clip
= g
.getClipBounds();
706 g
.setColor(getBackground());
707 g
.fillRect(clip
.x
, clip
.y
, clip
.width
, clip
.height
);
708 g
.setColor(oldColor
);
714 * Overridden to return <code>false</code>, since <code>JLayeredPane</code>
715 * cannot guarantee that its children don't overlap.
717 * @return <code>false</code>
719 public boolean isOptimizedDrawingEnabled()