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