GNU Classpath (0.19) | ||
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.Component; 42: import java.awt.Container; 43: import java.awt.Graphics; 44: import java.util.Hashtable; 45: import java.util.Iterator; 46: import java.util.Map; 47: import java.util.TreeMap; 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: */ 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: public 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 Integer FRAME_CONTENT_LAYER = new Integer (-30000); 151: 152: public static Integer DEFAULT_LAYER = new Integer (0); 153: public static Integer PALETTE_LAYER = new Integer (100); 154: public static Integer MODAL_LAYER = new Integer (200); 155: public static Integer POPUP_LAYER = new Integer (300); 156: public static Integer DRAG_LAYER = new Integer (400); 157: 158: TreeMap layers; // Layer Number (Integer) -> Layer Size (Integer) 159: Hashtable componentToLayer; // Component -> Layer Number (Integer) 160: 161: public JLayeredPane() 162: { 163: layers = new TreeMap (); 164: componentToLayer = new Hashtable (); 165: setLayout(null); 166: } 167: 168: /** 169: * Looks up the layer a child component is currently assigned to. 170: * 171: * @param c the component to look up. 172: * @return the layer the component is currently assigned to, in this container. 173: * @throws IllegalArgumentException if the component is not a child of this container. 174: */ 175: public int getLayer(Component c) 176: { 177: Component myComp = c; 178: while(! componentToLayer.containsKey(myComp)) 179: { 180: myComp = myComp.getParent(); 181: if (myComp == null) 182: break; 183: } 184: if (myComp == null) 185: throw new IllegalArgumentException 186: ("component is not in this JLayeredPane"); 187: Integer layerObj = (Integer) componentToLayer.get(myComp); 188: return layerObj.intValue(); 189: } 190: 191: /** 192: * Looks up the layer of <code>comp</code> in the component's nearest 193: * JLayeredPane ancestor. If <code>comp</code> is not contained 194: * in a JLayeredPane, the value 0 (default layer) is returned. 195: * 196: * @param comp the component for which the layer is looked up 197: * 198: * @return the layer of <code>comp</code> in its nearest JLayeredPane 199: * ancestor 200: */ 201: public static int getLayer(JComponent comp) 202: { 203: JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass 204: (JLayeredPane.class, comp); 205: if (lp == null) 206: return 0; 207: else 208: // The cast here forces the call to the instance method getLayer() 209: // instead of the static method (this would lead to infinite 210: // recursion). 211: return lp.getLayer((Component) comp); 212: } 213: 214: /** 215: * Returns the first JLayeredPane that contains the Component 216: * <code>comp</code> or <code>null</code> if <code>comp</code> is 217: * not contained in a JLayeredPane. 218: * 219: * @param comp the component for which we are searching the JLayeredPane 220: * ancestor 221: * 222: * @return the first JLayeredPane that contains the Component 223: * <code>comp</code> or <code>null</code> if <code>comp</code> is 224: * not contained in a JLayeredPane 225: */ 226: public static JLayeredPane getLayeredPaneAbove(Component comp) 227: { 228: JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass 229: (JLayeredPane.class, comp); 230: return lp; 231: } 232: 233: /** 234: * <p>Returns a pair of ints representing a half-open interval 235: * <code>[top, bottom)</code>, which is the range of component indices 236: * the provided layer number corresponds to.</p> 237: * 238: * <p>Note that "bottom" is <em>not</em> included in the interval of 239: * component indices in this layer: a layer with 0 elements in it has 240: * <code>ret[0] == ret[1]</code>.</p> 241: * 242: * @param layer the layer to look up. 243: * @return the half-open range of indices this layer spans. 244: * @throws IllegalArgumentException if layer does not refer to an active layer 245: * in this container. 246: */ 247: private int[] layerToRange (Integer layer) 248: { 249: int[] ret = new int[2]; 250: ret[1] = getComponents ().length; 251: Iterator i = layers.entrySet ().iterator (); 252: while (i.hasNext()) 253: { 254: Map.Entry pair = (Map.Entry) i.next(); 255: Integer layerNum = (Integer) pair.getKey (); 256: Integer layerSz = (Integer) pair.getValue (); 257: if (layerNum.intValue() == layer.intValue()) 258: { 259: ret[0] = ret[1] - layerSz.intValue (); 260: return ret; 261: } 262: else 263: { 264: ret[1] -= layerSz.intValue (); 265: } 266: } 267: // should have found the layer during iteration 268: throw new IllegalArgumentException (); 269: } 270: 271: /** 272: * Increments the recorded size of a given layer. 273: * 274: * @param layer the layer number to increment. 275: * @see #incrLayer 276: */ 277: private void incrLayer(Integer layer) 278: { 279: int sz = 1; 280: if (layers.containsKey (layer)) 281: sz += ((Integer)(layers.get (layer))).intValue (); 282: layers.put (layer, new Integer(sz)); 283: } 284: 285: /** 286: * Decrements the recorded size of a given layer. 287: * 288: * @param layer the layer number to decrement. 289: * @see #incrLayer 290: */ 291: private void decrLayer(Integer layer) 292: { 293: int sz = 0; 294: if (layers.containsKey (layer)) 295: sz = ((Integer)(layers.get (layer))).intValue () - 1; 296: layers.put (layer, new Integer(sz)); 297: } 298: 299: /** 300: * Return the greatest layer number currently in use, in this container. 301: * This number may legally be positive <em>or</em> negative. 302: * 303: * @return the least layer number. 304: * @see #lowestLayer() 305: */ 306: public int highestLayer() 307: { 308: if (layers.size() == 0) 309: return 0; 310: return ((Integer)(layers.lastKey ())).intValue (); 311: } 312: 313: /** 314: * Return the least layer number currently in use, in this container. 315: * This number may legally be positive <em>or</em> negative. 316: * 317: * @return the least layer number. 318: * @see #highestLayer() 319: */ 320: public int lowestLayer() 321: { 322: if (layers.size() == 0) 323: return 0; 324: return ((Integer)(layers.firstKey ())).intValue (); 325: } 326: 327: /** 328: * Moves a component to the "front" of its layer. The "front" is a 329: * synonym for position 0, which is also the last position drawn in each 330: * layer, so is usually the component which occludes the most other 331: * components in its layer. 332: * 333: * @param c the component to move to the front of its layer. 334: * @throws IllegalArgumentException if the component is not a child of 335: * this container. 336: * @see #moveToBack 337: */ 338: public void moveToFront(Component c) 339: { 340: setPosition (c, 0); 341: } 342: 343: /** 344: * <p>Moves a component to the "back" of its layer. The "back" is a 345: * synonym for position N-1 (also known as position -1), where N is the 346: * size of the layer.</p> 347: * 348: * <p>The "back" of a layer is the first position drawn, so the component at 349: * the "back" is usually the component which is occluded by the most 350: * other components in its layer.</p> 351: * 352: * @param c the component to move to the back of its layer. 353: * @throws IllegalArgumentException if the component is not a child of 354: * this container. 355: * @see #moveToFront 356: */ 357: public void moveToBack(Component c) 358: { 359: setPosition (c, -1); 360: } 361: 362: /** 363: * Return the position of a component within its layer. Positions are assigned 364: * from the "front" (position 0) to the "back" (position N-1), and drawn from 365: * the back towards the front. 366: * 367: * @param c the component to get the position of. 368: * @throws IllegalArgumentException if the component is not a child of 369: * this container. 370: * @see #setPosition 371: */ 372: public int getPosition(Component c) 373: { 374: int layer = getLayer (c); 375: int[] range = layerToRange(new Integer(layer)); 376: int top = range[0]; 377: int bot = range[1]; 378: Component[] comps = getComponents (); 379: for (int i = top; i < bot; ++i) 380: { 381: if (comps[i] == c) 382: return i - top; 383: } 384: // should have found it 385: throw new IllegalArgumentException (); 386: } 387: 388: /** 389: * Change the position of a component within its layer. Positions are assigned 390: * from the "front" (position 0) to the "back" (position N-1), and drawn from 391: * the back towards the front. 392: * 393: * @param c the component to change the position of. 394: * @param position the position to assign the component to. 395: * @throws IllegalArgumentException if the component is not a child of 396: * this container. 397: * @see #getPosition 398: */ 399: public void setPosition(Component c, int position) 400: { 401: int layer = getLayer (c); 402: int[] range = layerToRange(new Integer(layer)); 403: if (range[0] == range[1]) 404: throw new IllegalArgumentException (); 405: 406: int top = range[0]; 407: int bot = range[1]; 408: if (position == -1) 409: position = (bot - top) - 1; 410: int targ = Math.min(top + position, bot-1); 411: int curr = -1; 412: 413: Component[] comps = getComponents(); 414: for (int i = top; i < bot; ++i) 415: { 416: if (comps[i] == c) 417: { 418: curr = i; 419: break; 420: } 421: } 422: if (curr == -1) 423: // should have found it 424: throw new IllegalArgumentException(); 425: 426: super.swapComponents (curr, targ); 427: revalidate(); 428: repaint(); 429: } 430: 431: /** 432: * Return an array of all components within a layer of this 433: * container. Components are ordered front-to-back, with the "front" 434: * element (which draws last) at position 0 of the returned array. 435: * 436: * @param layer the layer to return components from. 437: * @return the components in the layer. 438: */ 439: public Component[] getComponentsInLayer(int layer) 440: { 441: int[] range = layerToRange (getObjectForLayer (layer)); 442: if (range[0] == range[1]) 443: return new Component[0]; 444: else 445: { 446: Component[] comps = getComponents (); 447: int sz = range[1] - range[0]; 448: Component[] nc = new Component[sz]; 449: for (int i = 0; i < sz; ++i) 450: nc[i] = comps[range[0] + i]; 451: return nc; 452: } 453: } 454: 455: /** 456: * Return the number of components within a layer of this 457: * container. 458: * 459: * @param layer the layer count components in. 460: * @return the number of components in the layer. 461: */ 462: public int getComponentCountInLayer(int layer) 463: { 464: int[] range = layerToRange (getObjectForLayer (layer)); 465: if (range[0] == range[1]) 466: return 0; 467: else 468: return (range[1] - range[0]); 469: } 470: 471: /** 472: * Return a hashtable mapping child components of this container to 473: * Integer objects representing the component's layer assignments. 474: */ 475: protected Hashtable getComponentToLayer() 476: { 477: return componentToLayer; 478: } 479: 480: /** 481: * Return the index of a component within the underlying (contiguous) 482: * array of children. This is a "raw" number which does not represent the 483: * child's position in a layer, but rather its position in the logical 484: * drawing order of all children of the container. 485: * 486: * @param c the component to look up. 487: * @return the external index of the component. 488: * @throws IllegalArgumentException if the component is not a child of 489: * this container. 490: */ 491: public int getIndexOf(Component c) 492: { 493: int layer = getLayer (c); 494: int[] range = layerToRange(new Integer(layer)); 495: Component[] comps = getComponents(); 496: for (int i = range[0]; i < range[1]; ++i) 497: { 498: if (comps[i] == c) 499: return i; 500: } 501: // should have found the component during iteration 502: throw new IllegalArgumentException (); 503: } 504: 505: /** 506: * Return an Integer object which holds the same int value as the 507: * parameter. This is strictly an optimization to minimize the number of 508: * identical Integer objects which we allocate. 509: * 510: * @param layer the layer number as an int. 511: * @return the layer number as an Integer, possibly shared. 512: */ 513: protected Integer getObjectForLayer(int layer) 514: { 515: switch (layer) 516: { 517: case -30000: 518: return FRAME_CONTENT_LAYER; 519: 520: case 0: 521: return DEFAULT_LAYER; 522: 523: case 100: 524: return PALETTE_LAYER; 525: 526: case 200: 527: return MODAL_LAYER; 528: 529: case 300: 530: return POPUP_LAYER; 531: 532: case 400: 533: return DRAG_LAYER; 534: 535: default: 536: break; 537: } 538: 539: return new Integer(layer); 540: } 541: 542: /** 543: * Computes an index at which to request the superclass {@link 544: * java.awt.Container} inserts a component, given an abstract layer and 545: * position number. 546: * 547: * @param layer the layer in which to insert a component. 548: * @param position the position in the layer at which to insert a component. 549: * @return the index at which to insert the component. 550: */ 551: protected int insertIndexForLayer(int layer, int position) 552: { 553: 554: Integer lobj = getObjectForLayer (layer); 555: if (! layers.containsKey(lobj)) 556: layers.put (lobj, new Integer (0)); 557: int[] range = layerToRange (lobj); 558: if (range[0] == range[1]) 559: return range[0]; 560: 561: int top = range[0]; 562: int bot = range[1]; 563: 564: if (position == -1 || position > (bot - top)) 565: return bot; 566: else 567: return top + position; 568: } 569: 570: /** 571: * Removes a child from this container. The child is specified by 572: * index. After removal, the child no longer occupies a layer. 573: * 574: * @param index the index of the child component to remove. 575: */ 576: public void remove(int index) 577: { 578: Component c = getComponent(index); 579: int layer = getLayer(c); 580: decrLayer(new Integer(layer)); 581: componentToLayer.remove(c); 582: super.remove(index); 583: // FIXME: Figure out if this call is correct. 584: revalidate(); 585: } 586: 587: /** 588: * <p>Set the layer property for a component, within this container. The 589: * component will be implicitly mapped to the bottom-most position in the 590: * layer, but only if added <em>after</em> calling this method.</p> 591: * 592: * <p>Read that carefully: this method should be called <em>before</em> the 593: * component is added to the container.</p> 594: * 595: * @param c the component to set the layer property for. 596: * @param layer the layer number to assign to the component. 597: */ 598: public void setLayer(Component c, int layer) 599: { 600: componentToLayer.put (c, getObjectForLayer (layer)); 601: } 602: 603: /** 604: * Set the layer and position of a component, within this container. 605: * 606: * @param c the child component to set the layer property for. 607: * @param layer the layer number to assign to the component. 608: * @param position the position number to assign to the component. 609: */ 610: public void setLayer(Component c, 611: int layer, 612: int position) 613: { 614: remove(c); 615: add(c, getObjectForLayer (layer)); 616: setPosition(c, position); 617: revalidate(); 618: repaint(); 619: } 620: 621: /** 622: * Overrides the default implementation from {@link java.awt.Container} 623: * such that <code>layerConstraint</code> is interpreted as an {@link 624: * Integer}, specifying the layer to which the component will be added 625: * (at the bottom position). 626: * 627: * @param comp the component to add. 628: * @param layerConstraint an integer specifying the layer to add the component to. 629: * @param index an ignored parameter, for compatibility. 630: */ 631: protected void addImpl(Component comp, Object layerConstraint, int index) 632: { 633: Integer layer; 634: if (layerConstraint != null && layerConstraint instanceof Integer) 635: layer = (Integer) layerConstraint; 636: else if (componentToLayer.containsKey (comp)) 637: layer = (Integer) componentToLayer.remove (comp); 638: else 639: layer = DEFAULT_LAYER; 640: 641: int newIdx = insertIndexForLayer(layer.intValue (), index); 642: 643: componentToLayer.put (comp, layer); 644: incrLayer (layer); 645: 646: super.addImpl(comp, null, newIdx); 647: } 648: 649: /** 650: * Sets the layer property for a JComponent. 651: * 652: * @param component the component for which to set the layer 653: * @param layer the layer property to set 654: */ 655: public static void putLayer(JComponent component, int layer) 656: { 657: getLayeredPaneAbove(component).setLayer(component, layer); 658: } 659: 660: /** 661: * Returns the accessible context for this <code>JLayeredPane</code>. 662: * 663: * @return the accessible context for this <code>JLayeredPane</code> 664: */ 665: public AccessibleContext getAccessibleContext() 666: { 667: if (accessibleContext == null) 668: accessibleContext = new AccessibleJLayeredPane(); 669: return accessibleContext; 670: } 671: 672: /** 673: * This method is overridden order to provide a reasonable painting 674: * mechanism for <code>JLayeredPane</code>. This is necessary since 675: * <code>JLayeredPane</code>'s do not have an own UI delegate. 676: * 677: * Basically this method clears the background for the 678: * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>. 679: * 680: * @param g the graphics context to use 681: */ 682: public void paint(Graphics g) 683: { 684: g.setColor(getBackground()); 685: g.fillRect(0, 0, getWidth(), getHeight()); 686: super.paint(g); 687: } 688: 689: /** 690: * Overridden to return <code>false</code>, since <code>JLayeredPane</code> 691: * cannot guarantee that its children don't overlap. 692: * 693: * @return <code>false</code> 694: */ 695: public boolean isOptimizedDrawingEnabled() 696: { 697: return false; 698: } 699: }
GNU Classpath (0.19) |