GNU Classpath (0.91) | |
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.Hashtable; 47: 48: import javax.accessibility.Accessible; 49: import javax.accessibility.AccessibleContext; 50: import javax.accessibility.AccessibleRole; 51: 52: /** 53: * A container that adds depth to the usual <code>Container</code> semantics. 54: * Each child component of a <code>Layered Pane</code> is placed within one 55: * of several layers. <code>JLayeredPane</code> defines a set of standard 56: * layers. The pre-defined sets are (in the order from button to top): 57: * 58: * <dl> 59: * <dt>{@link #DEFAULT_LAYER}</dt> 60: * <dd>The layer where most of the normal components are placed. This 61: * is the bottommost layer.</dd> 62: * 63: * <dt>{@link #PALETTE_LAYER}</dt> 64: * <dd>Palette windows are placed in this layer.</dd> 65: * 66: * <dt>{@link #MODAL_LAYER}</dt> 67: * <dd>The layer where internal modal dialog windows are placed.</dd> 68: * 69: * <dt>{@link #POPUP_LAYER}</dt> 70: * <dd>The layer for popup menus</dd> 71: * 72: * <dt>{@link #DRAG_LAYER}</dt> 73: * <dd>Components that are beeing dragged are temporarily placed in 74: * this layer.</dd> 75: * </dl> 76: * 77: * <p>A child is in exactly one of these layers at any time, though there may 78: * be other layers if someone creates them.</p> 79: * 80: * <p>You can add a component to a specific layer using the 81: * {@link Container#add(Component, Object)} method. I.e. 82: * <code>layeredPane.add(comp, JLayeredPane.MODAL_LAYER)</code> will add the 83: * component <code>comp</code> to the modal layer of <code>layeredPane</code>. 84: * </p> 85: * 86: * <p>To change the layer of a component that is already a child of 87: * a <code>JLayeredPane</code>, use the {@link #setLayer(Component, int)} 88: * method.</p> 89: * 90: * <p>The purpose of this class is to translate this view of "layers" into a 91: * contiguous array of components: the one held in our ancestor, 92: * {@link java.awt.Container}.</p> 93: * 94: * <p>There is a precise set of words we will use to refer to numbers within 95: * this class:</p> 96: * 97: * <dl> 98: * <dt>Component Index:</dt> 99: * <dd>An offset into the <code>component</code> array held in our ancestor, 100: * {@link java.awt.Container}, from <code>[0 .. component.length)</code>. The drawing 101: * rule with indices is that 0 is drawn last.</dd> 102: * 103: * <dt>Layer Number:</dt> 104: * <dd>A general <code>int</code> specifying a layer within this component. Negative 105: * numbers are drawn first, then layer 0, then positive numbered layers, in 106: * ascending order.</dd> 107: * 108: * <dt>Position:</dt> 109: * <dd>An offset into a layer's "logical drawing order". Layer position 0 110: * is drawn last. Layer position -1 is a synonym for the first layer 111: * position (the logical "bottom").</dd> 112: * </dl> 113: * 114: * <p><b>Note:</b> the layer numbering order is the <em>reverse</em> of the 115: * component indexing and position order</p> 116: * 117: * @author Graydon Hoare (graydon@redhat.com) 118: * @author Roman Kennke (kennke@aicas.com) 119: */ 120: public class JLayeredPane extends JComponent implements Accessible 121: { 122: 123: /** 124: * Provides accessibility support for <code>JLayeredPane</code>. 125: */ 126: protected class AccessibleJLayeredPane extends AccessibleJComponent 127: { 128: /** 129: * Creates a new instance of <code>AccessibleJLayeredPane</code>. 130: */ 131: protected AccessibleJLayeredPane() 132: { 133: // Nothing to do here. 134: } 135: 136: /** 137: * Returns the accessble role of <code>JLayeredPane</code>, 138: * {@link AccessibleRole#LAYERED_PANE}. 139: */ 140: public AccessibleRole getAccessibleRole() 141: { 142: return AccessibleRole.LAYERED_PANE; 143: } 144: } 145: 146: private static final long serialVersionUID = 5534920399324590459L; 147: 148: public static final String LAYER_PROPERTY = "layeredContainerLayer"; 149: 150: public static final Integer FRAME_CONTENT_LAYER = new Integer (-30000); 151: 152: public static final Integer DEFAULT_LAYER = new Integer (0); 153: public static final Integer PALETTE_LAYER = new Integer (100); 154: public static final Integer MODAL_LAYER = new Integer (200); 155: public static final Integer POPUP_LAYER = new Integer (300); 156: public static final Integer DRAG_LAYER = new Integer (400); 157: 158: private Hashtable componentToLayer; // Component -> Layer Number (Integer) 159: 160: public JLayeredPane() 161: { 162: componentToLayer = new Hashtable (); 163: setLayout(null); 164: } 165: 166: /** 167: * Looks up the layer a child component is currently assigned to. 168: * 169: * If <code>c</code> is an instance of {@link JComponent}, then the layer 170: * is fetched from the client property with the key {@link #LAYER_PROPERTY}. 171: * Otherwise it is looked up in an internal hashtable that maps 172: * non-JComponent components to layers. If the components cannot be found 173: * in either way, the {@link #DEFAULT_LAYER} is returned. 174: * 175: * @param c the component to look up. 176: * 177: * @return the layer the component is currently assigned to; if the component 178: * is not in this layered pane, then 0 (DEFAULT_LAYER) is returned 179: */ 180: public int getLayer(Component c) 181: { 182: Integer layerObj; 183: if (c instanceof JComponent) 184: { 185: JComponent jc = (JComponent) c; 186: layerObj = (Integer) jc.getClientProperty(LAYER_PROPERTY); 187: } 188: else 189: layerObj = (Integer) componentToLayer.get(c); 190: 191: if (layerObj == null) 192: layerObj = DEFAULT_LAYER; 193: 194: return layerObj.intValue(); 195: } 196: 197: /** 198: * Looks up the layer in the client property with the key 199: * {@link #LAYER_PROPERTY} of <code>comp</code>. If no such property can be 200: * found, we return <code>0</code> ({@link #DEFAULT_LAYER}). 201: * 202: * @param comp the component for which the layer is looked up 203: * 204: * @return the layer of <code>comp</code> as stored in the corresponding 205: * client property, or <code>0</code> if there is no such property 206: */ 207: public static int getLayer(JComponent comp) 208: { 209: Integer layerObj = (Integer) comp.getClientProperty(LAYER_PROPERTY); 210: if (layerObj == null) 211: layerObj = DEFAULT_LAYER; 212: return layerObj.intValue(); 213: } 214: 215: /** 216: * Returns the first JLayeredPane that contains the Component 217: * <code>comp</code> or <code>null</code> if <code>comp</code> is 218: * not contained in a JLayeredPane. 219: * 220: * @param comp the component for which we are searching the JLayeredPane 221: * ancestor 222: * 223: * @return the first JLayeredPane that contains the Component 224: * <code>comp</code> or <code>null</code> if <code>comp</code> is 225: * not contained in a JLayeredPane 226: */ 227: public static JLayeredPane getLayeredPaneAbove(Component comp) 228: { 229: JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass 230: (JLayeredPane.class, comp); 231: return lp; 232: } 233: 234: /** 235: * Return the greatest layer number currently in use, in this container. 236: * This number may legally be positive <em>or</em> negative. 237: * 238: * @return the highest layer number 239: * 240: * @see #lowestLayer() 241: */ 242: public int highestLayer() 243: { 244: Component[] components = getComponents(); 245: int highest; 246: if (components.length == 0) 247: highest = 0; 248: else 249: { 250: highest = Integer.MIN_VALUE; 251: for (int i = 0; i < components.length; i++) 252: highest = Math.max(highest, getLayer(components[i])); 253: } 254: return highest; 255: } 256: 257: /** 258: * Return the least layer number currently in use, in this container. 259: * This number may legally be positive <em>or</em> negative. 260: * 261: * @return the least layer number 262: * 263: * @see #highestLayer() 264: */ 265: public int lowestLayer() 266: { 267: Component[] components = getComponents(); 268: int lowest; 269: if (components.length == 0) 270: lowest = 0; 271: else 272: { 273: lowest = Integer.MAX_VALUE; 274: for (int i = 0; i < components.length; i++) 275: lowest = Math.max(lowest, getLayer(components[i])); 276: } 277: return lowest; 278: } 279: 280: /** 281: * Moves a component to the "front" of its layer. The "front" is a 282: * synonym for position 0, which is also the last position drawn in each 283: * layer, so is usually the component which occludes the most other 284: * components in its layer. 285: * 286: * @param c the component to move to the front of its layer 287: * 288: * @see #moveToBack 289: */ 290: public void moveToFront(Component c) 291: { 292: setPosition (c, 0); 293: } 294: 295: /** 296: * <p>Moves a component to the "back" of its layer. The "back" is a 297: * synonym for position N-1 (also known as position -1), where N is the 298: * size of the layer.</p> 299: * 300: * <p>The "back" of a layer is the first position drawn, so the component at 301: * the "back" is usually the component which is occluded by the most 302: * other components in its layer.</p> 303: * 304: * @param c the component to move to the back of its layer. 305: * 306: * @see #moveToFront 307: */ 308: public void moveToBack(Component c) 309: { 310: setPosition (c, -1); 311: } 312: 313: /** 314: * Return the position of a component within its layer. Positions are assigned 315: * from the "front" (position 0) to the "back" (position N-1), and drawn from 316: * the back towards the front. 317: * 318: * @param c the component to get the position of 319: * 320: * @return the position of <code>c</code> within its layer or -1 if 321: * <code>c</code> is not a child of this layered pane 322: * 323: * @see #setPosition 324: */ 325: public int getPosition(Component c) 326: { 327: int pos = -1; 328: int index = getIndexOf(c); 329: Component[] components = getComponents(); 330: int layer = getLayer(c); 331: if (index >= 0) 332: { 333: for (int i = index; i >= 0; --i) 334: { 335: if (layer == getLayer(components[i])) 336: pos++; 337: else 338: break; 339: } 340: } 341: return pos; 342: } 343: 344: /** 345: * Change the position of a component within its layer. Positions are assigned 346: * from the "front" (position 0) to the "back" (position N-1), and drawn from 347: * the back towards the front. 348: * 349: * @param c the component to change the position of 350: * @param position the position to assign the component to 351: * 352: * @see #getPosition 353: */ 354: public void setPosition(Component c, int position) 355: { 356: int layer = getLayer(c); 357: int index = insertIndexForLayer(layer, position); 358: setComponentZOrder(c, index); 359: } 360: 361: /** 362: * Return an array of all components within a layer of this 363: * container. Components are ordered front-to-back, with the "front" 364: * element (which draws last) at position 0 of the returned array. 365: * 366: * @param layer the layer to return components from 367: * 368: * @return the components in the layer 369: */ 370: public Component[] getComponentsInLayer(int layer) 371: { 372: Component[] inLayer = new Component[getComponentCountInLayer(layer)]; 373: Component[] components = getComponents(); 374: int j = 0; 375: for (int i = 0; i < components.length; ++i) 376: { 377: if (layer == getLayer(components[i])) 378: { 379: inLayer[j] = components[i]; 380: j++; 381: } 382: } 383: return inLayer; 384: } 385: 386: /** 387: * Return the number of components within a layer of this 388: * container. 389: * 390: * @param layer the layer count components in 391: * 392: * @return the number of components in the layer 393: */ 394: public int getComponentCountInLayer(int layer) 395: { 396: Component[] components = getComponents(); 397: int count = 0; 398: for (int i = components.length - 1; i >= 0; --i) 399: { 400: if (getLayer(components[i]) == layer) 401: count++; 402: } 403: return count; 404: } 405: 406: /** 407: * Return a hashtable mapping child components of this container to 408: * Integer objects representing the component's layer assignments. 409: */ 410: protected Hashtable getComponentToLayer() 411: { 412: return componentToLayer; 413: } 414: 415: /** 416: * Return the index of a component within the underlying (contiguous) 417: * array of children. This is a "raw" number which does not represent the 418: * child's position in a layer, but rather its position in the logical 419: * drawing order of all children of the container. 420: * 421: * @param c the component to look up. 422: * 423: * @return the external index of the component or <code>-1</code> if 424: * <code>c</code> is not a child of this layered pane 425: */ 426: public int getIndexOf(Component c) 427: { 428: return getComponentZOrder(c); 429: } 430: 431: /** 432: * Return an Integer object which holds the same int value as the 433: * parameter. This is strictly an optimization to minimize the number of 434: * identical Integer objects which we allocate. 435: * 436: * @param layer the layer number as an int. 437: * 438: * @return the layer number as an Integer, possibly shared. 439: */ 440: protected Integer getObjectForLayer(int layer) 441: { 442: switch (layer) 443: { 444: case -30000: 445: return FRAME_CONTENT_LAYER; 446: 447: case 0: 448: return DEFAULT_LAYER; 449: 450: case 100: 451: return PALETTE_LAYER; 452: 453: case 200: 454: return MODAL_LAYER; 455: 456: case 300: 457: return POPUP_LAYER; 458: 459: case 400: 460: return DRAG_LAYER; 461: 462: default: 463: break; 464: } 465: 466: return new Integer(layer); 467: } 468: 469: /** 470: * Computes an index at which to request the superclass {@link 471: * java.awt.Container} inserts a component, given an abstract layer and 472: * position number. 473: * 474: * @param layer the layer in which to insert a component. 475: * @param position the position in the layer at which to insert a component. 476: * 477: * @return the index at which to insert the component. 478: */ 479: protected int insertIndexForLayer(int layer, int position) 480: { 481: // position < 0 means insert at greatest position within layer. 482: if (position < 0) 483: position = Integer.MAX_VALUE; 484: 485: Component[] components = getComponents(); 486: int index = 0; 487: 488: // Try to find the start index of the specified layer. 489: int p = -1; 490: for (int i = 0; i < components.length; i++) 491: { 492: int l = getLayer(components[i]); 493: if (l > layer) 494: index++; 495: // If we are in the layer we look for, try to find the position. 496: else if (l == layer) 497: { 498: p++; 499: if (p < position) 500: index++; 501: else 502: break; 503: } 504: // No need to look further if the layer at i is smaller than layer. 505: else 506: break; 507: } 508: return index; 509: } 510: 511: /** 512: * Removes a child from this container. The child is specified by 513: * index. After removal, the child no longer occupies a layer. 514: * 515: * @param index the index of the child component to remove. 516: */ 517: public void remove(int index) 518: { 519: Component c = getComponent(index); 520: if (! (c instanceof JComponent)) 521: componentToLayer.remove(c); 522: super.remove(index); 523: } 524: 525: /** 526: * Removes all components from this container. 527: * 528: * @since 1.5 529: */ 530: public void removeAll() 531: { 532: componentToLayer.clear(); 533: super.removeAll(); 534: } 535: 536: /** 537: * <p>Set the layer property for a component, within this container. The 538: * component will be implicitly mapped to the bottom-most position in the 539: * layer, but only if added <em>after</em> calling this method.</p> 540: * 541: * <p>Read that carefully: this method should be called <em>before</em> the 542: * component is added to the container.</p> 543: * 544: * @param c the component to set the layer property for. 545: * @param layer the layer number to assign to the component. 546: */ 547: public void setLayer(Component c, int layer) 548: { 549: setLayer(c, layer, -1); 550: } 551: 552: /** 553: * Set the layer and position of a component, within this container. 554: * 555: * @param c the child component to set the layer property for. 556: * @param layer the layer number to assign to the component. 557: * @param position the position number to assign to the component. 558: */ 559: public void setLayer(Component c, int layer, int position) 560: { 561: Integer layerObj = getObjectForLayer(layer); 562: if (c instanceof JComponent) 563: { 564: JComponent jc = (JComponent) c; 565: jc.putClientProperty(LAYER_PROPERTY, layerObj); 566: } 567: else 568: componentToLayer.put (c, layerObj); 569: 570: // Set position only of component is already added to this layered pane. 571: if (getIndexOf(c) != -1) 572: setPosition(c, position); 573: } 574: 575: /** 576: * Overrides the default implementation from {@link java.awt.Container} 577: * such that <code>layerConstraint</code> is interpreted as an {@link 578: * Integer}, specifying the layer to which the component will be added 579: * (at the bottom position). 580: * 581: * The argument <code>index</code> specifies the position within the layer 582: * at which the component should be added, where <code>0</code> is the top 583: * position greater values specify positions below that and <code>-1</code> 584: * specifies the bottom position. 585: * 586: * @param comp the component to add 587: * @param layerConstraint an integer specifying the layer to add the 588: * component to 589: * @param index the position within the layer 590: */ 591: protected void addImpl(Component comp, Object layerConstraint, int index) 592: { 593: int layer; 594: if (layerConstraint != null && layerConstraint instanceof Integer) 595: layer = ((Integer) layerConstraint).intValue(); 596: else 597: layer = getLayer(comp); 598: 599: int newIdx = insertIndexForLayer(layer, index); 600: setLayer(comp, layer); 601: super.addImpl(comp, layerConstraint, newIdx); 602: repaint(comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight()); 603: } 604: 605: /** 606: * Sets the layer property for a JComponent. 607: * 608: * @param component the component for which to set the layer 609: * @param layer the layer property to set 610: */ 611: public static void putLayer(JComponent component, int layer) 612: { 613: component.putClientProperty(LAYER_PROPERTY, new Integer(layer)); 614: } 615: 616: /** 617: * Returns the accessible context for this <code>JLayeredPane</code>. 618: * 619: * @return the accessible context for this <code>JLayeredPane</code> 620: */ 621: public AccessibleContext getAccessibleContext() 622: { 623: if (accessibleContext == null) 624: accessibleContext = new AccessibleJLayeredPane(); 625: return accessibleContext; 626: } 627: 628: /** 629: * This method is overridden order to provide a reasonable painting 630: * mechanism for <code>JLayeredPane</code>. This is necessary since 631: * <code>JLayeredPane</code>'s do not have an own UI delegate. 632: * 633: * Basically this method clears the background for the 634: * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>. 635: * 636: * @param g the graphics context to use 637: */ 638: public void paint(Graphics g) 639: { 640: if (isOpaque()) 641: { 642: Color oldColor = g.getColor(); 643: Rectangle clip = g.getClipBounds(); 644: g.setColor(getBackground()); 645: g.fillRect(clip.x, clip.y, clip.width, clip.height); 646: g.setColor(oldColor); 647: } 648: super.paint(g); 649: } 650: 651: /** 652: * Returns <code>false</code> if components in this layered pane can overlap, 653: * otherwise <code>true</code>. 654: * 655: * @return <code>false</code> if components in this layered pane can overlap, 656: * otherwise <code>true</code> 657: */ 658: public boolean isOptimizedDrawingEnabled() 659: { 660: int numChildren = getComponentCount(); 661: boolean result = true; 662: for (int i = 0; i < numChildren; ++i) 663: { 664: Component c1 = getComponent(i); 665: if (! c1.isVisible()) 666: continue; 667: Rectangle r1 = c1.getBounds(); 668: if (r1.isEmpty()) 669: continue; 670: 671: for (int j = i + 1; j < numChildren; ++j) 672: { 673: Component c2 = getComponent(j); 674: if (! c2.isVisible()) 675: continue; 676: Rectangle r2 = c2.getBounds(); 677: if (r2.isEmpty()) 678: continue; 679: if (r1.intersects(r2)) 680: { 681: result = false; 682: break; 683: } 684: if (result == false) 685: break; 686: } 687: } 688: return result; 689: } 690: }
GNU Classpath (0.91) |