001    /* JLayeredPane.java --
002       Copyright (C) 2002, 2004 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing;
040    
041    import java.awt.Color;
042    import java.awt.Component;
043    import java.awt.Container;
044    import java.awt.Graphics;
045    import java.awt.Rectangle;
046    import java.util.ArrayList;
047    import java.util.Hashtable;
048    
049    import javax.accessibility.Accessible;
050    import javax.accessibility.AccessibleContext;
051    import 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     */
121    public 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    }