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