Frames | No Frames |
1: /* JLayeredPane.java -- 2: Copyright (C) 2002, 2004 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 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. 10: 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. 15: 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. 20: 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. 25: 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. */ 37: 38: 39: package javax.swing; 40: 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.util.ArrayList; 47: import java.util.Hashtable; 48: 49: import javax.accessibility.Accessible; 50: import javax.accessibility.AccessibleContext; 51: import javax.accessibility.AccessibleRole; 52: 53: /** 54: * A container that adds depth to the usual <code>Container</code> semantics. 55: * Each child component of a <code>Layered Pane</code> is placed within one 56: * of several layers. <code>JLayeredPane</code> defines a set of standard 57: * layers. The pre-defined sets are (in the order from button to top): 58: * 59: * <dl> 60: * <dt>{@link #DEFAULT_LAYER}</dt> 61: * <dd>The layer where most of the normal components are placed. This 62: * is the bottommost layer.</dd> 63: * 64: * <dt>{@link #PALETTE_LAYER}</dt> 65: * <dd>Palette windows are placed in this layer.</dd> 66: * 67: * <dt>{@link #MODAL_LAYER}</dt> 68: * <dd>The layer where internal modal dialog windows are placed.</dd> 69: * 70: * <dt>{@link #POPUP_LAYER}</dt> 71: * <dd>The layer for popup menus</dd> 72: * 73: * <dt>{@link #DRAG_LAYER}</dt> 74: * <dd>Components that are beeing dragged are temporarily placed in 75: * this layer.</dd> 76: * </dl> 77: * 78: * <p>A child is in exactly one of these layers at any time, though there may 79: * be other layers if someone creates them.</p> 80: * 81: * <p>You can add a component to a specific layer using the 82: * {@link Container#add(Component, Object)} method. I.e. 83: * <code>layeredPane.add(comp, JLayeredPane.MODAL_LAYER)</code> will add the 84: * component <code>comp</code> to the modal layer of <code>layeredPane</code>. 85: * </p> 86: * 87: * <p>To change the layer of a component that is already a child of 88: * a <code>JLayeredPane</code>, use the {@link #setLayer(Component, int)} 89: * method.</p> 90: * 91: * <p>The purpose of this class is to translate this view of "layers" into a 92: * contiguous array of components: the one held in our ancestor, 93: * {@link java.awt.Container}.</p> 94: * 95: * <p>There is a precise set of words we will use to refer to numbers within 96: * this class:</p> 97: * 98: * <dl> 99: * <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: }