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., 59 Temple Place, Suite 330, 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. */
40 import java
.awt
.Component
;
41 import java
.util
.Hashtable
;
42 import java
.util
.Iterator
;
44 import java
.util
.TreeMap
;
45 import javax
.accessibility
.Accessible
;
48 * <p>The "Layered Pane" is a container which divides its children into 6 (or
49 * more) disjoint sets. the pre-defined sets are:</p>
52 * <li>"Frame Content"</li>
60 * <p>A child is in exactly one of these layers at any time, though there may
61 * be other layers if someone creates them.</p>
63 * <p>The purpose of this class is to translate this view of "layers" into a
64 * contiguous array of components: the one held in our ancestor,
65 * {@link java.awt.Container}.</p>
67 * <p>There is a precise set of words we will use to refer to numbers within
71 * <dt>Component Index:</dt>
72 * <dd>An offset into the <code>component</code> array held in our ancestor,
73 * {@link java.awt.Container}, from <code>[0 .. component.length)</code>. The drawing
74 * rule with indices is that 0 is drawn last.</dd>
76 * <dt>Layer Number:</dt>
77 * <dd>A general <code>int</code> specifying a layer within this component. Negative
78 * numbers are drawn first, then layer 0, then positive numbered layers, in
79 * ascending order.</dd>
82 * <dd>An offset into a layer's "logical drawing order". Layer position 0
83 * is drawn last. Layer position -1 is a synonym for the first layer
84 * position (the logical "bottom").</dd>
86 * <p><b>Note:</b> the layer numbering order is the <em>reverse</em> of the
87 * component indexing and position order</p>
89 * @author Graydon Hoare <graydon@redhat.com>
92 public class JLayeredPane
extends JComponent
implements Accessible
95 public static String LAYER_PROPERTY
= "LAYER_PROPERTY";
97 public static Integer FRAME_CONTENT_LAYER
= new Integer (-30000);
99 public static Integer DEFAULT_LAYER
= new Integer (0);
100 public static Integer PALETTE_LAYER
= new Integer (100);
101 public static Integer MODAL_LAYER
= new Integer (200);
102 public static Integer POPUP_LAYER
= new Integer (300);
103 public static Integer DRAG_LAYER
= new Integer (400);
105 TreeMap layers
; // Layer Number (Integer) -> Layer Size (Integer)
106 Hashtable componentToLayer
; // Component -> Layer Number (Integer)
110 layers
= new TreeMap ();
111 componentToLayer
= new Hashtable ();
116 * Looks up the layer a child component is currently assigned to.
118 * @param c the component to look up.
119 * @return the layer the component is currently assigned to, in this container.
120 * @throws IllegalArgumentException if the component is not a child of this container.
123 protected Integer
getLayer (Component c
)
125 if (! componentToLayer
.containsKey (c
))
126 throw new IllegalArgumentException ();
127 return (Integer
) componentToLayer
.get (c
);
131 * <p>Returns a pair of ints representing a half-open interval
132 * <code>[top, bottom)</code>, which is the range of component indices
133 * the provided layer number corresponds to.</p>
135 * <p>Note that "bottom" is <em>not</em> included in the interval of
136 * component indices in this layer: a layer with 0 elements in it has
137 * <code>ret[0] == ret[1]</code>.</p>
139 * @param layer the layer to look up.
140 * @return the half-open range of indices this layer spans.
141 * @throws IllegalArgumentException if layer does not refer to an active layer
145 protected int[] layerToRange (Integer layer
)
147 int[] ret
= new int[2];
148 ret
[1] = getComponents ().length
;
149 Iterator i
= layers
.entrySet ().iterator ();
152 Map
.Entry pair
= (Map
.Entry
) i
.next();
153 Integer layerNum
= (Integer
) pair
.getKey ();
154 Integer layerSz
= (Integer
) pair
.getValue ();
155 if (layerNum
== layer
)
157 ret
[0] = ret
[1] - layerSz
.intValue ();
162 ret
[1] -= layerSz
.intValue ();
165 // should have found the layer during iteration
166 throw new IllegalArgumentException ();
170 * Increments the recorded size of a given layer.
172 * @param layer the layer number to increment.
176 protected void incrLayer(Integer layer
)
179 if (layers
.containsKey (layer
))
180 sz
+= ((Integer
)(layers
.get (layer
))).intValue ();
181 layers
.put (layer
, new Integer(sz
));
185 * Decrements the recorded size of a given layer.
187 * @param layer the layer number to decrement.
191 protected void decrLayer(Integer layer
)
194 if (layers
.containsKey (layer
))
195 sz
= ((Integer
)(layers
.get (layer
))).intValue () - 1;
196 layers
.put (layer
, new Integer(sz
));
200 * Return the greatest layer number currently in use, in this container.
201 * This number may legally be positive <em>or</em> negative.
203 * @return the least layer number.
204 * @see #lowestLayer()
207 public int highestLayer()
209 if (layers
.size() == 0)
211 return ((Integer
)(layers
.lastKey ())).intValue ();
215 * Return the least layer number currently in use, in this container.
216 * This number may legally be positive <em>or</em> negative.
218 * @return the least layer number.
219 * @see #highestLayer()
222 public int lowestLayer()
224 if (layers
.size() == 0)
226 return ((Integer
)(layers
.firstKey ())).intValue ();
230 * Moves a component to the "front" of its layer. The "front" is a
231 * synonym for position 0, which is also the last position drawn in each
232 * layer, so is usually the component which occludes the most other
233 * components in its layer.
235 * @param c the component to move to the front of its layer.
236 * @throws IllegalArgumentException if the component is not a child of
241 public void moveToFront(Component c
)
247 * <p>Moves a component to the "back" of its layer. The "back" is a
248 * synonym for position N-1 (also known as position -1), where N is the
249 * size of the layer.</p>
251 * <p>The "back" of a layer is the first position drawn, so the component at
252 * the "back" is usually the component which is occluded by the most
253 * other components in its layer.</p>
255 * @param c the component to move to the back of its layer.
256 * @throws IllegalArgumentException if the component is not a child of
258 * @see #moveToFront()
261 public void moveToBack(Component c
)
267 * Return the position of a component within its layer. Positions are assigned
268 * from the "front" (position 0) to the "back" (position N-1), and drawn from
269 * the back towards the front.
271 * @param c the component to get the position of.
272 * @throws IllegalArgumentException if the component is not a child of
274 * @see #setPosition()
277 public int getPosition(Component c
)
279 Integer layer
= getLayer (c
);
280 int[] range
= layerToRange (layer
);
283 Component
[] comps
= getComponents ();
284 for (int i
= top
; i
< bot
; ++i
)
289 // should have found it
290 throw new IllegalArgumentException ();
294 * Change the position of a component within its layer. Positions are assigned
295 * from the "front" (position 0) to the "back" (position N-1), and drawn from
296 * the back towards the front.
298 * @param c the component to change the position of.
299 * @param position the position to assign the component to.
300 * @throws IllegalArgumentException if the component is not a child of
302 * @see #getPosition()
305 public void setPosition(Component c
, int position
)
307 Integer layer
= getLayer (c
);
308 int[] range
= layerToRange (layer
);
309 if (range
[0] == range
[1])
310 throw new IllegalArgumentException ();
315 position
= (bot
- top
) - 1;
316 int targ
= top
+ position
;
319 Component
[] comps
= getComponents();
320 for (int i
= top
; i
< bot
; ++i
)
329 // should have found it
330 throw new IllegalArgumentException ();
332 super.swapComponents (curr
, targ
);
338 * Return an array of all components within a layer of this
339 * container. Components are ordered front-to-back, with the "front"
340 * element (which draws last) at position 0 of the returned array.
342 * @param layer the layer to return components from.
343 * @return the components in the layer.
346 public Component
[] getComponentsInLayer(int layer
)
348 int[] range
= layerToRange (getObjectForLayer (layer
));
349 if (range
[0] == range
[1])
350 return new Component
[0];
353 Component
[] comps
= getComponents ();
354 int sz
= range
[1] - range
[0];
355 Component
[] nc
= new Component
[sz
];
356 for (int i
= 0; i
< sz
; ++i
)
357 nc
[i
] = comps
[range
[0] + i
];
363 * Return the number of components within a layer of this
366 * @param layer the layer count components in.
367 * @return the number of components in the layer.
370 public int getComponentCountInLayer(int layer
)
372 int[] range
= layerToRange (getObjectForLayer (layer
));
373 if (range
[0] == range
[1])
376 return (range
[1] - range
[0]);
380 * Return a hashtable mapping child components of this container to
381 * Integer objects representing the component's layer assignments.
384 protected Hashtable
getComponentToLayer()
386 return componentToLayer
;
390 * Return the index of a component within the underlying (contiguous)
391 * array of children. This is a "raw" number which does not represent the
392 * child's position in a layer, but rather its position in the logical
393 * drawing order of all children of the container.
395 * @param c the component to look up.
396 * @return the external index of the component.
397 * @throws IllegalArgumentException if the component is not a child of
401 public int getIndexOf(Component c
)
403 Integer layer
= getLayer (c
);
404 int[] range
= layerToRange (layer
);
405 Component
[] comps
= getComponents();
406 for (int i
= range
[0]; i
< range
[1]; ++i
)
411 // should have found the component during iteration
412 throw new IllegalArgumentException ();
416 * Return an Integer object which holds the same int value as the
417 * parameter. This is strictly an optimization to minimize the number of
418 * identical Integer objects which we allocate.
420 * @param layer the layer number as an int.
421 * @return the layer number as an Integer, possibly shared.
424 protected Integer
getObjectForLayer(int layer
)
429 return FRAME_CONTENT_LAYER
;
432 return DEFAULT_LAYER
;
435 return PALETTE_LAYER
;
450 return new Integer(layer
);
454 * Computes an index at which to request the superclass {@link
455 * java.awt.Container} inserts a component, given an abstract layer and
458 * @param layer the layer in which to insert a component.
459 * @param position the position in the layer at which to insert a component.
460 * @return the index at which to insert the component.
463 protected int insertIndexForLayer(int layer
, int position
)
466 Integer lobj
= getObjectForLayer (layer
);
467 if (! layers
.containsKey(lobj
))
468 layers
.put (lobj
, new Integer (0));
469 int[] range
= layerToRange (lobj
);
470 if (range
[0] == range
[1])
476 if (position
== -1 || position
> (bot
- top
))
479 return top
+ position
;
483 * Removes a child from this container. The child is specified by
484 * index. After removal, the child no longer occupies a layer.
486 * @param index the index of the child component to remove.
489 public void remove (int index
)
491 Component c
= getComponent (index
);
492 Integer layer
= getLayer (c
);
494 componentToLayer
.remove (c
);
495 super.remove (index
);
499 * Removes a child from this container. The child is specified directly.
500 * After removal, the child no longer occupies a layer.
502 * @param comp the child to remove.
505 public void remove (Component comp
)
507 remove (getIndexOf (comp
));
511 * <p>Set the layer property for a component, within this container. The
512 * component will be implicitly mapped to the bottom-most position in the
513 * layer, but only if added <em>after</em> calling this method.</p>
515 * <p>Read that carefully: this method should be called <em>before</em> the
516 * component is added to the container.</p>
518 * @param c the component to set the layer property for.
519 * @param layer the layer number to assign to the component.
522 public void setLayer(Component c
, int layer
)
524 componentToLayer
.put (c
, getObjectForLayer (layer
));
528 * Set the layer and position of a component, within this container.
530 * @param c the child component to set the layer property for.
531 * @param layer the layer number to assign to the component.
532 * @param position the position number to assign to the component.
535 public void setLayer(Component c
,
539 componentToLayer
.put (c
, getObjectForLayer (layer
));
540 setPosition(c
, position
);
546 * Overrides the default implementation from {@link java.awt.Container}
547 * such that <code>layerConstraint</code> is interpreted as an {@link
548 * Integer}, specifying the layer to which the component will be added
549 * (at the bottom position).
551 * @param comp the component to add.
552 * @param layerConstraint an integer specifying the layer to add the component to.
553 * @param index an ignored parameter, for compatibility.
556 protected void addImpl(Component comp
, Object layerConstraint
, int index
)
559 if (layerConstraint
!= null && layerConstraint
instanceof Integer
)
560 layer
= (Integer
) layerConstraint
;
561 else if (componentToLayer
.containsKey (comp
))
562 layer
= (Integer
) componentToLayer
.remove (comp
);
564 layer
= DEFAULT_LAYER
;
566 int newIdx
= insertIndexForLayer(layer
.intValue (), -1);
568 componentToLayer
.put (comp
, layer
);
571 super.addImpl(comp
, null, newIdx
);