Merge from mainline
[official-gcc.git] / libjava / classpath / javax / swing / JLayeredPane.java
blobdc8b10d2178d2cd236d8051b64e993a35e55eddf
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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. */
39 package javax.swing;
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;
49 import java.util.Map;
50 import java.util.TreeMap;
52 import javax.accessibility.Accessible;
53 import javax.accessibility.AccessibleContext;
54 import javax.accessibility.AccessibleRole;
56 /**
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):
62 * <dl>
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
78 * this layer.</dd>
79 * </dl>
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>.
88 * </p>
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)}
92 * method.</p>
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
99 * this class:</p>
101 * <dl>
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>
112 * <dt>Position:</dt>
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>
116 * </dl>
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 ();
170 setLayout(null);
173 /**
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();
186 if (myComp == null)
187 break;
189 if (myComp == null)
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
204 * ancestor
206 public static int getLayer(JComponent comp)
208 JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass
209 (JLayeredPane.class, comp);
210 if (lp == null)
211 return 0;
212 else
213 // The cast here forces the call to the instance method getLayer()
214 // instead of the static method (this would lead to infinite
215 // recursion).
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
225 * ancestor
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);
235 return lp;
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
250 * in this container.
252 private int[] layerToRange (Integer layer)
254 int[] ret = new int[2];
255 ret[1] = getComponents ().length;
256 Iterator i = layers.entrySet ().iterator ();
257 while (i.hasNext())
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 ();
266 break;
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())
273 ret[1] = ret[0];
274 break;
276 else
278 ret[1] -= layerSz.intValue ();
281 return ret;
285 * Increments the recorded size of a given layer.
287 * @param layer the layer number to increment.
288 * @see #incrLayer
290 private void incrLayer(Integer layer)
292 int sz = 1;
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.
302 * @see #incrLayer
304 private void decrLayer(Integer layer)
306 int sz = 0;
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)
322 return 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)
336 return 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
348 * this container.
349 * @see #moveToBack
351 public void moveToFront(Component c)
353 setPosition (c, 0);
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
367 * this container.
368 * @see #moveToFront
370 public void moveToBack(Component c)
372 setPosition (c, -1);
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
382 * this container.
383 * @see #setPosition
385 public int getPosition(Component c)
387 int layer = getLayer (c);
388 int[] range = layerToRange(new Integer(layer));
389 int top = range[0];
390 int bot = range[1];
391 Component[] comps = getComponents ();
392 for (int i = top; i < bot; ++i)
394 if (comps[i] == c)
395 return i - top;
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
409 * this container.
410 * @see #getPosition
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 ();
419 int top = range[0];
420 int bot = range[1];
421 if (position == -1)
422 position = (bot - top) - 1;
423 int targ = Math.min(top + position, bot-1);
424 int curr = -1;
426 Component[] comps = getComponents();
427 for (int i = top; i < bot; ++i)
429 if (comps[i] == c)
431 curr = i;
432 break;
435 if (curr == -1)
436 // should have found it
437 throw new IllegalArgumentException();
439 if (curr == 0)
440 super.swapComponents(curr, targ);
441 else
442 while (curr > 0)
443 super.swapComponents (curr, --curr);
445 revalidate();
446 repaint();
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];
462 else
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];
469 return nc;
474 * Return the number of components within a layer of this
475 * container.
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])
484 return 0;
485 else
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
507 * this container.
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)
516 if (comps[i] == c)
517 return 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)
533 switch (layer)
535 case -30000:
536 return FRAME_CONTENT_LAYER;
538 case 0:
539 return DEFAULT_LAYER;
541 case 100:
542 return PALETTE_LAYER;
544 case 200:
545 return MODAL_LAYER;
547 case 300:
548 return POPUP_LAYER;
550 case 400:
551 return DRAG_LAYER;
553 default:
554 break;
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
563 * position number.
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])
577 return range[0];
579 int top = range[0];
580 int bot = range[1];
582 if (position == -1 || position > (bot - top))
583 return bot;
584 else
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);
600 super.remove(index);
601 // FIXME: Figure out if this call is correct.
602 revalidate();
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,
629 int layer,
630 int position)
632 remove(c);
633 add(c, getObjectForLayer (layer));
634 setPosition(c, position);
635 revalidate();
636 repaint();
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)
651 Integer layer;
652 if (layerConstraint != null && layerConstraint instanceof Integer)
653 layer = (Integer) layerConstraint;
654 else if (componentToLayer.containsKey (comp))
655 layer = (Integer) componentToLayer.remove (comp);
656 else
657 layer = DEFAULT_LAYER;
659 int newIdx = insertIndexForLayer(layer.intValue (), index);
661 componentToLayer.put (comp, layer);
662 incrLayer (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)
702 if (isOpaque())
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);
710 super.paint(g);
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()
721 return false;