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