001/* JLayeredPane.java -- 
002   Copyright (C) 2002, 2004 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing;
040
041import java.awt.Color;
042import java.awt.Component;
043import java.awt.Container;
044import java.awt.Graphics;
045import java.awt.Rectangle;
046import java.util.ArrayList;
047import java.util.Hashtable;
048
049import javax.accessibility.Accessible;
050import javax.accessibility.AccessibleContext;
051import javax.accessibility.AccessibleRole;
052
053/**
054 * A container that adds depth to the usual <code>Container</code> semantics.
055 * Each child component of a <code>Layered Pane</code> is placed within one
056 * of several layers. <code>JLayeredPane</code> defines a set of standard
057 * layers. The pre-defined sets are (in the order from button to top):
058 *
059 *  <dl>
060 *    <dt>{@link #DEFAULT_LAYER}</dt>
061 *    <dd>The layer where most of the normal components are placed. This
062 *      is the bottommost layer.</dd>
063 *
064 *    <dt>{@link #PALETTE_LAYER}</dt>
065 *    <dd>Palette windows are placed in this layer.</dd>
066 *
067 *    <dt>{@link #MODAL_LAYER}</dt>
068 *    <dd>The layer where internal modal dialog windows are placed.</dd>
069 *
070 *    <dt>{@link #POPUP_LAYER}</dt>
071 *    <dd>The layer for popup menus</dd>
072 *
073 *    <dt>{@link #DRAG_LAYER}</dt>
074 *    <dd>Components that are beeing dragged are temporarily placed in
075 *       this layer.</dd>
076 *  </dl>
077 *
078 * <p>A child is in exactly one of these layers at any time, though there may
079 * be other layers if someone creates them.</p>
080 *
081 * <p>You can add a component to a specific layer using the
082 * {@link Container#add(Component, Object)} method. I.e.
083 * <code>layeredPane.add(comp, JLayeredPane.MODAL_LAYER)</code> will add the
084 * component <code>comp</code> to the modal layer of <code>layeredPane</code>.
085 * </p>
086 *
087 * <p>To change the layer of a component that is already a child of
088 * a <code>JLayeredPane</code>, use the {@link #setLayer(Component, int)} 
089 * method.</p>
090 *
091 * <p>The purpose of this class is to translate this view of "layers" into a
092 * contiguous array of components: the one held in our ancestor,
093 * {@link java.awt.Container}.</p>
094 *
095 * <p>There is a precise set of words we will use to refer to numbers within
096 * this class:</p>
097 * 
098 * <dl>
099 * <dt>Component Index:</dt> 
100 * <dd>An offset into the <code>component</code> array held in our ancestor,
101 * {@link java.awt.Container}, from <code>[0 .. component.length)</code>. The drawing
102 * rule with indices is that 0 is drawn last.</dd>
103 *
104 * <dt>Layer Number:</dt>
105 * <dd>A general <code>int</code> specifying a layer within this component.  Negative
106 * numbers are drawn first, then layer 0, then positive numbered layers, in
107 * ascending order.</dd>
108 *
109 * <dt>Position:</dt> 
110 * <dd>An offset into a layer's "logical drawing order". Layer position 0
111 * is drawn last. Layer position -1 is a synonym for the first layer
112 * position (the logical "bottom").</dd>
113 * </dl>
114 *
115 * <p><b>Note:</b> the layer numbering order is the <em>reverse</em> of the
116 * component indexing and position order</p>
117 *
118 * @author Graydon Hoare (graydon@redhat.com)
119 * @author Roman Kennke (kennke@aicas.com)
120 */
121public class JLayeredPane extends JComponent implements Accessible
122{
123  
124  /**
125   * Provides accessibility support for <code>JLayeredPane</code>.
126   */
127  protected class AccessibleJLayeredPane extends AccessibleJComponent
128  {
129    /**
130     * Creates a new instance of <code>AccessibleJLayeredPane</code>.
131     */
132    protected AccessibleJLayeredPane()
133    {
134      // Nothing to do here.
135    }
136
137    /**
138     * Returns the accessble role of <code>JLayeredPane</code>,
139     * {@link AccessibleRole#LAYERED_PANE}. 
140     */
141    public AccessibleRole getAccessibleRole()
142    {
143      return AccessibleRole.LAYERED_PANE;
144    }
145  }
146
147  private static final long serialVersionUID = 5534920399324590459L;
148  
149  public static final String LAYER_PROPERTY = "layeredContainerLayer";
150
151  public static final Integer FRAME_CONTENT_LAYER = new Integer(-30000);
152
153  public static final Integer DEFAULT_LAYER = new Integer(0);
154  public static final Integer PALETTE_LAYER = new Integer(100);
155  public static final Integer MODAL_LAYER   = new Integer(200);
156  public static final Integer POPUP_LAYER   = new Integer(300);
157  public static final Integer DRAG_LAYER    = new Integer(400);
158
159  private Hashtable componentToLayer;   // Component -> Layer Number (Integer)
160
161  public JLayeredPane()
162  {
163    componentToLayer = new Hashtable();
164    setLayout(null);
165  }
166
167  /** 
168   * Looks up the layer a child component is currently assigned to.
169   *
170   * If <code>c</code> is an instance of {@link JComponent}, then the layer
171   * is fetched from the client property with the key {@link #LAYER_PROPERTY}.
172   * Otherwise it is looked up in an internal hashtable that maps
173   * non-JComponent components to layers. If the components cannot be found
174   * in either way, the {@link #DEFAULT_LAYER} is returned.
175   *
176   * @param c the component to look up.
177   *
178   * @return the layer the component is currently assigned to; if the component
179   *         is not in this layered pane, then 0 (DEFAULT_LAYER) is returned
180   */
181  public int getLayer(Component c)
182  {
183    Integer layerObj;
184    if (c instanceof JComponent)
185      {
186        JComponent jc = (JComponent) c;
187        layerObj = (Integer) jc.getClientProperty(LAYER_PROPERTY);
188      }
189    else
190      layerObj = (Integer) componentToLayer.get(c);
191
192    if (layerObj == null)
193      layerObj = DEFAULT_LAYER;
194
195    return layerObj.intValue();
196  }
197
198  /**
199   * Looks up the layer in the client property with the key
200   * {@link #LAYER_PROPERTY} of <code>comp</code>. If no such property can be
201   * found, we return <code>0</code> ({@link #DEFAULT_LAYER}).
202   * 
203   * @param comp the component for which the layer is looked up
204   *
205   * @return the layer of <code>comp</code> as stored in the corresponding
206   *         client property, or <code>0</code> if there is no such property
207   */
208  public static int getLayer(JComponent comp)
209  {
210    Integer layerObj = (Integer) comp.getClientProperty(LAYER_PROPERTY);
211    if (layerObj == null)
212      layerObj = DEFAULT_LAYER;
213    return layerObj.intValue();
214  }
215
216  /**
217   * Returns the first JLayeredPane that contains the Component
218   * <code>comp</code> or <code>null</code> if <code>comp</code> is
219   * not contained in a JLayeredPane.
220   *
221   * @param comp the component for which we are searching the JLayeredPane
222   *     ancestor
223   *
224   * @return the first JLayeredPane that contains the Component
225   *     <code>comp</code> or <code>null</code> if <code>comp</code> is
226   *     not contained in a JLayeredPane
227   */
228  public static JLayeredPane getLayeredPaneAbove(Component comp)
229  {
230    JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass
231      (JLayeredPane.class, comp);
232    return lp;
233  }
234
235  /**
236   * Return the greatest layer number currently in use, in this container.
237   * This number may legally be positive <em>or</em> negative.
238   *
239   * @return the highest layer number
240   *
241   * @see #lowestLayer()
242   */
243  public int highestLayer()
244  {
245    Component[] components = getComponents();
246    int highest;
247    if (components.length == 0)
248      highest = 0;
249    else
250      {
251        highest = Integer.MIN_VALUE;
252        for (int i = 0; i < components.length; i++)
253          highest = Math.max(highest, getLayer(components[i]));
254      }
255    return highest;
256  }
257
258  /**
259   * Return the least layer number currently in use, in this container.
260   * This number may legally be positive <em>or</em> negative.
261   *
262   * @return the least layer number
263   *
264   * @see #highestLayer()
265   */
266  public int lowestLayer()
267  {
268    Component[] components = getComponents();
269    int lowest;
270    if (components.length == 0)
271      lowest = 0;
272    else
273      {
274        lowest = Integer.MAX_VALUE;
275        for (int i = 0; i < components.length; i++)
276          lowest = Math.max(lowest, getLayer(components[i]));
277      }
278    return lowest;
279  }
280
281  /**
282   * Moves a component to the "front" of its layer. The "front" is a
283   * synonym for position 0, which is also the last position drawn in each
284   * layer, so is usually the component which occludes the most other
285   * components in its layer.
286   *
287   * @param c the component to move to the front of its layer
288   *
289   * @see #moveToBack
290   */
291  public void moveToFront(Component c)
292  {
293    setPosition (c, 0);
294  }
295
296  /**
297   * <p>Moves a component to the "back" of its layer. The "back" is a
298   * synonym for position N-1 (also known as position -1), where N is the
299   * size of the layer.</p>
300   *
301   * <p>The "back" of a layer is the first position drawn, so the component at
302   * the "back" is usually the component which is occluded by the most
303   * other components in its layer.</p>
304   *
305   * @param c the component to move to the back of its layer.
306   *
307   * @see #moveToFront
308   */
309  public void moveToBack(Component c)
310  {
311    setPosition (c, -1);
312  }
313
314  /**
315   * Return the position of a component within its layer. Positions are assigned
316   * from the "front" (position 0) to the "back" (position N-1), and drawn from 
317   * the back towards the front.
318   *
319   * @param c the component to get the position of
320   *
321   * @return the position of <code>c</code> within its layer or -1 if
322   *         <code>c</code> is not a child of this layered pane
323   *
324   * @see #setPosition
325   */
326  public int getPosition(Component c)
327  {
328    int pos = -1;
329    int index = getIndexOf(c);
330    if (index >= 0)
331      {
332        pos = 0;
333        int layer = getLayer(c);
334        for (int i = index - 1; i >= 0; --i)
335          {
336            if (layer == getLayer(getComponent(i)))
337              pos++;
338            else
339              break;
340          }
341      }
342    return pos;
343  }
344
345  /**
346   * Change the position of a component within its layer. Positions are assigned
347   * from the "front" (position 0) to the "back" (position N-1), and drawn from 
348   * the back towards the front.
349   *
350   * @param c the component to change the position of
351   * @param position the position to assign the component to
352   *
353   * @see #getPosition
354   */
355  public void setPosition(Component c, int position)
356  {
357    setLayer(c, getLayer(c), position);
358  }
359    
360  /**
361   * Return an array of all components within a layer of this
362   * container. Components are ordered front-to-back, with the "front"
363   * element (which draws last) at position 0 of the returned array.
364   *
365   * @param layer the layer to return components from
366   *
367   * @return the components in the layer
368   */
369  public Component[] getComponentsInLayer(int layer)
370  {
371    Component[] inLayer = new Component[getComponentCountInLayer(layer)];
372    Component[] components = getComponents();
373    int j = 0;
374    for (int i = 0; i < components.length; ++i)
375      {
376        if (layer == getLayer(components[i]))
377          {
378            inLayer[j] = components[i];
379            j++;
380          }
381      }
382    return inLayer;
383  }
384
385  /**
386   * Return the number of components within a layer of this
387   * container.
388   *
389   * @param layer the layer count components in
390   *
391   * @return the number of components in the layer
392   */
393  public int getComponentCountInLayer(int layer)
394  {
395    Component[] components = getComponents();
396    int count = 0;
397    for (int i = components.length - 1; i >= 0; --i)
398      {
399        if (getLayer(components[i]) == layer)
400          count++;
401      }
402    return count;
403  }
404
405  /**
406   * Return a hashtable mapping child components of this container to
407   * Integer objects representing the component's layer assignments.
408   */
409  protected Hashtable<Component, Integer> getComponentToLayer()
410  {
411    return componentToLayer;
412  }
413
414  /**
415   * Return the index of a component within the underlying (contiguous)
416   * array of children. This is a "raw" number which does not represent the
417   * child's position in a layer, but rather its position in the logical
418   * drawing order of all children of the container.
419   *
420   * @param c the component to look up.
421   *
422   * @return the external index of the component or <code>-1</code> if
423   *         <code>c</code> is not a child of this layered pane 
424   */
425  public int getIndexOf(Component c) 
426  {
427    return getComponentZOrder(c);
428  }
429
430  /**
431   * Return an Integer object which holds the same int value as the
432   * parameter. This is strictly an optimization to minimize the number of
433   * identical Integer objects which we allocate.
434   *
435   * @param layer the layer number as an int.
436   *
437   * @return the layer number as an Integer, possibly shared.
438   */
439  protected Integer getObjectForLayer(int layer)
440  {
441    switch (layer)
442            {
443            case -30000:
444        return FRAME_CONTENT_LAYER;
445
446            case 0:
447        return DEFAULT_LAYER;
448
449            case 100:
450        return PALETTE_LAYER;
451
452            case 200:
453        return MODAL_LAYER;
454
455            case 300:
456        return POPUP_LAYER;
457
458            case 400:
459        return DRAG_LAYER;
460
461            default:
462        break;
463            }
464
465    return new Integer(layer);
466  }
467
468  /**
469   * Computes an index at which to request the superclass {@link
470   * java.awt.Container} inserts a component, given an abstract layer and
471   * position number.
472   *
473   * @param layer the layer in which to insert a component.
474   * @param position the position in the layer at which to insert a component.
475   *
476   * @return the index at which to insert the component.
477   */
478  protected int insertIndexForLayer(int layer, int position)
479  {
480    return insertIndexForLayer(null, layer, position);
481  }
482
483  /**
484   * Similar to {@link #insertIndexForLayer(int, int)}, only that it takes a
485   * component parameter, which should be ignored in the search. This is
486   * necessary to support {@link #setLayer(Component, int, int)} which uses
487   * Container.setComponentZOrder(), which doesn't remove the component.
488   *
489   * @param comp the component to ignore
490   * @param layer the layer
491   * @param position the position
492   *
493   * @return the insertion index
494   */
495  private int insertIndexForLayer(Component comp, int layer, int position)
496  {
497    // Create the component list to search through.
498    ArrayList l = new ArrayList();
499    int count = getComponentCount();
500    for (int i = 0; i < count; i++)
501      {
502        Component c = getComponent(i);
503        if (c != comp)
504          l.add(c);
505      }
506
507    count = l.size();
508    int layerStart = -1;
509    int layerEnd = -1;
510    for (int i = 0; i < count; i++)
511      {
512        int layerOfComponent = getLayer((Component) l.get(i));
513        if (layerStart == -1 && layerOfComponent == layer)
514          layerStart = i;
515        if (layerOfComponent < layer)
516          {
517            // We are beyond the layer that we are looking for. Update the
518            // layerStart and layerEnd and exit the loop.
519            if (i == 0)
520              {
521                layerStart = 0;
522                layerEnd = 0;
523              }
524            else
525              layerEnd = i;
526            break;
527          }
528      }
529
530    // No layer found. The requested layer is lower than any existing layer,
531    // put the component at the end.
532    int insertIndex;
533    if (layerStart == -1 && layerEnd == -1)
534      {
535        insertIndex = count;
536      }
537    else
538      {
539        // Corner cases.
540        if (layerStart != -1 && layerEnd == -1)
541          layerEnd = count;
542        if (layerStart == -1 && layerEnd != -1)
543          layerStart = layerEnd;
544
545        // Adding to the bottom of a layer returns the end index
546        // in the layer.
547        if (position == -1)
548          insertIndex = layerEnd;
549        else
550          {
551            // Insert into a layer.
552            if (position > -1 && layerStart + position <= layerEnd)
553              insertIndex = layerStart + position;
554            else
555              insertIndex = layerEnd;
556          }
557      }
558    return insertIndex;
559  }
560
561  /**
562   * Removes a child from this container. The child is specified by
563   * index. After removal, the child no longer occupies a layer.
564   *
565   * @param index the index of the child component to remove.
566   */
567  public void remove(int index)
568  {
569    Component c = getComponent(index);
570    if (! (c instanceof JComponent))
571      componentToLayer.remove(c);
572    super.remove(index);
573  }
574
575  /**
576   * Removes all components from this container.
577   *
578   * @since 1.5
579   */
580  public void removeAll()
581  {
582        componentToLayer.clear();
583        super.removeAll();
584  }
585
586  /**
587   * <p>Set the layer property for a component, within this container. The
588   * component will be implicitly mapped to the bottom-most position in the
589   * layer, but only if added <em>after</em> calling this method.</p>
590   *
591   * <p>Read that carefully: this method should be called <em>before</em> the
592   * component is added to the container.</p>
593   *
594   * @param c the component to set the layer property for.
595   * @param layer the layer number to assign to the component.
596   */
597  public void setLayer(Component c, int layer)
598  {
599    setLayer(c, layer, -1);
600  }
601
602  /**
603   * Set the layer and position of a component, within this container.
604   *
605   * @param c the child component to set the layer property for.
606   * @param layer the layer number to assign to the component.
607   * @param position the position number to assign to the component.
608   */
609  public void setLayer(Component c, int layer, int position)
610  {
611    Integer layerObj = getObjectForLayer(layer);
612
613    // Nothing to do if neither the layer nor the position is
614    // changed.
615    if (layer != getLayer(c) || position != getPosition(c))
616      {
617        // Store the layer either in the JComponent or in the hashtable
618        if (c instanceof JComponent)
619          {
620            JComponent jc = (JComponent) c;
621            jc.putClientProperty(LAYER_PROPERTY, layerObj);
622          }
623        else
624          componentToLayer.put (c, layerObj);
625
626        // Update the component in the Z order of the Container.
627        Container parent = c.getParent();
628        if (parent == this)
629          {
630            int index = insertIndexForLayer(c, layer, position);
631            setComponentZOrder(c, index);
632          }
633      }
634    repaint(c.getX(), c.getY(), c.getWidth(), c.getHeight());
635  }
636
637  /**
638   * Overrides the default implementation from {@link java.awt.Container}
639   * such that <code>layerConstraint</code> is interpreted as an {@link
640   * Integer}, specifying the layer to which the component will be added
641   * (at the bottom position).
642   *
643   * The argument <code>index</code> specifies the position within the layer
644   * at which the component should be added, where <code>0</code> is the top
645   * position greater values specify positions below that and <code>-1</code>
646   * specifies the bottom position.
647   *
648   * @param comp the component to add
649   * @param layerConstraint an integer specifying the layer to add the
650   *        component to
651   * @param index the position within the layer
652   */
653  protected void addImpl(Component comp, Object layerConstraint, int index) 
654  {
655    int layer;
656    if (layerConstraint != null && layerConstraint instanceof Integer)
657      {
658        layer = ((Integer) layerConstraint).intValue();
659        setLayer(comp, layer);
660      }
661    else
662      layer = getLayer(comp);
663
664    int newIdx = insertIndexForLayer(layer, index);
665    super.addImpl(comp, layerConstraint, newIdx);
666    comp.validate();
667    comp.repaint();
668  }
669
670  /**
671   * Sets the layer property for a JComponent.
672   *
673   * @param component the component for which to set the layer
674   * @param layer the layer property to set
675   */
676  public static void putLayer(JComponent component, int layer)
677  {
678    component.putClientProperty(LAYER_PROPERTY, new Integer(layer));
679  }
680
681  /**
682   * Returns the accessible context for this <code>JLayeredPane</code>.
683   *
684   * @return the accessible context for this <code>JLayeredPane</code>
685   */
686  public AccessibleContext getAccessibleContext()
687  {
688    if (accessibleContext == null)
689      accessibleContext = new AccessibleJLayeredPane();
690    return accessibleContext;
691  }
692
693  /**
694   * This method is overridden order to provide a reasonable painting
695   * mechanism for <code>JLayeredPane</code>. This is necessary since
696   * <code>JLayeredPane</code>'s do not have an own UI delegate.
697   *
698   * Basically this method clears the background for the
699   * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>.
700   *
701   * @param g the graphics context to use
702   */
703  public void paint(Graphics g)
704  {
705    if (isOpaque())
706      {
707        Color oldColor = g.getColor();
708        Rectangle clip = g.getClipBounds();
709        g.setColor(getBackground());
710        g.fillRect(clip.x, clip.y, clip.width, clip.height);
711        g.setColor(oldColor);
712      }
713    super.paint(g);
714  }
715
716  /**
717   * Returns <code>false</code> if components in this layered pane can overlap,
718   * otherwise <code>true</code>.
719   *
720   * @return <code>false</code> if components in this layered pane can overlap,
721   *         otherwise <code>true</code>
722   */
723  public boolean isOptimizedDrawingEnabled()
724  {
725    int numChildren = getComponentCount();
726    boolean result = true;
727    for (int i = 0; i < numChildren; ++i)
728      {
729        Component c1 = getComponent(i);
730        if (! c1.isVisible())
731          continue;
732        Rectangle r1 = c1.getBounds();
733        if (r1.isEmpty())
734          continue;
735
736        for (int j = i + 1; j < numChildren; ++j)
737          {
738            Component c2 = getComponent(j);
739            if (! c2.isVisible())
740              continue;
741            Rectangle r2 = c2.getBounds();
742            if (r2.isEmpty())
743              continue;
744            if (r1.intersects(r2))
745              {
746                result = false;
747                break;
748              }
749            if (result == false)
750              break;
751          }
752      }
753    return result;
754  }
755}