001 /* JLayeredPane.java -- 002 Copyright (C) 2002, 2004 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package javax.swing; 040 041 import java.awt.Color; 042 import java.awt.Component; 043 import java.awt.Container; 044 import java.awt.Graphics; 045 import java.awt.Rectangle; 046 import java.util.ArrayList; 047 import java.util.Hashtable; 048 049 import javax.accessibility.Accessible; 050 import javax.accessibility.AccessibleContext; 051 import javax.accessibility.AccessibleRole; 052 053 /** 054 * A container that adds depth to the usual <code>Container</code> semantics. 055 * Each child component of a <code>Layered Pane</code> is placed within one 056 * of several layers. <code>JLayeredPane</code> defines a set of standard 057 * layers. The pre-defined sets are (in the order from button to top): 058 * 059 * <dl> 060 * <dt>{@link #DEFAULT_LAYER}</dt> 061 * <dd>The layer where most of the normal components are placed. This 062 * is the bottommost layer.</dd> 063 * 064 * <dt>{@link #PALETTE_LAYER}</dt> 065 * <dd>Palette windows are placed in this layer.</dd> 066 * 067 * <dt>{@link #MODAL_LAYER}</dt> 068 * <dd>The layer where internal modal dialog windows are placed.</dd> 069 * 070 * <dt>{@link #POPUP_LAYER}</dt> 071 * <dd>The layer for popup menus</dd> 072 * 073 * <dt>{@link #DRAG_LAYER}</dt> 074 * <dd>Components that are beeing dragged are temporarily placed in 075 * this layer.</dd> 076 * </dl> 077 * 078 * <p>A child is in exactly one of these layers at any time, though there may 079 * be other layers if someone creates them.</p> 080 * 081 * <p>You can add a component to a specific layer using the 082 * {@link Container#add(Component, Object)} method. I.e. 083 * <code>layeredPane.add(comp, JLayeredPane.MODAL_LAYER)</code> will add the 084 * component <code>comp</code> to the modal layer of <code>layeredPane</code>. 085 * </p> 086 * 087 * <p>To change the layer of a component that is already a child of 088 * a <code>JLayeredPane</code>, use the {@link #setLayer(Component, int)} 089 * method.</p> 090 * 091 * <p>The purpose of this class is to translate this view of "layers" into a 092 * contiguous array of components: the one held in our ancestor, 093 * {@link java.awt.Container}.</p> 094 * 095 * <p>There is a precise set of words we will use to refer to numbers within 096 * this class:</p> 097 * 098 * <dl> 099 * <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 * @author Roman Kennke (kennke@aicas.com) 120 */ 121 public class JLayeredPane extends JComponent implements Accessible 122 { 123 124 /** 125 * Provides accessibility support for <code>JLayeredPane</code>. 126 */ 127 protected class AccessibleJLayeredPane extends AccessibleJComponent 128 { 129 /** 130 * Creates a new instance of <code>AccessibleJLayeredPane</code>. 131 */ 132 protected AccessibleJLayeredPane() 133 { 134 // Nothing to do here. 135 } 136 137 /** 138 * Returns the accessble role of <code>JLayeredPane</code>, 139 * {@link AccessibleRole#LAYERED_PANE}. 140 */ 141 public AccessibleRole getAccessibleRole() 142 { 143 return AccessibleRole.LAYERED_PANE; 144 } 145 } 146 147 private static final long serialVersionUID = 5534920399324590459L; 148 149 public static final String LAYER_PROPERTY = "layeredContainerLayer"; 150 151 public static final Integer FRAME_CONTENT_LAYER = new Integer(-30000); 152 153 public static final Integer DEFAULT_LAYER = new Integer(0); 154 public static final Integer PALETTE_LAYER = new Integer(100); 155 public static final Integer MODAL_LAYER = new Integer(200); 156 public static final Integer POPUP_LAYER = new Integer(300); 157 public static final Integer DRAG_LAYER = new Integer(400); 158 159 private Hashtable componentToLayer; // Component -> Layer Number (Integer) 160 161 public JLayeredPane() 162 { 163 componentToLayer = new Hashtable(); 164 setLayout(null); 165 } 166 167 /** 168 * Looks up the layer a child component is currently assigned to. 169 * 170 * If <code>c</code> is an instance of {@link JComponent}, then the layer 171 * is fetched from the client property with the key {@link #LAYER_PROPERTY}. 172 * Otherwise it is looked up in an internal hashtable that maps 173 * non-JComponent components to layers. If the components cannot be found 174 * in either way, the {@link #DEFAULT_LAYER} is returned. 175 * 176 * @param c the component to look up. 177 * 178 * @return the layer the component is currently assigned to; if the component 179 * is not in this layered pane, then 0 (DEFAULT_LAYER) is returned 180 */ 181 public int getLayer(Component c) 182 { 183 Integer layerObj; 184 if (c instanceof JComponent) 185 { 186 JComponent jc = (JComponent) c; 187 layerObj = (Integer) jc.getClientProperty(LAYER_PROPERTY); 188 } 189 else 190 layerObj = (Integer) componentToLayer.get(c); 191 192 if (layerObj == null) 193 layerObj = DEFAULT_LAYER; 194 195 return layerObj.intValue(); 196 } 197 198 /** 199 * Looks up the layer in the client property with the key 200 * {@link #LAYER_PROPERTY} of <code>comp</code>. If no such property can be 201 * found, we return <code>0</code> ({@link #DEFAULT_LAYER}). 202 * 203 * @param comp the component for which the layer is looked up 204 * 205 * @return the layer of <code>comp</code> as stored in the corresponding 206 * client property, or <code>0</code> if there is no such property 207 */ 208 public static int getLayer(JComponent comp) 209 { 210 Integer layerObj = (Integer) comp.getClientProperty(LAYER_PROPERTY); 211 if (layerObj == null) 212 layerObj = DEFAULT_LAYER; 213 return layerObj.intValue(); 214 } 215 216 /** 217 * Returns the first JLayeredPane that contains the Component 218 * <code>comp</code> or <code>null</code> if <code>comp</code> is 219 * not contained in a JLayeredPane. 220 * 221 * @param comp the component for which we are searching the JLayeredPane 222 * ancestor 223 * 224 * @return the first JLayeredPane that contains the Component 225 * <code>comp</code> or <code>null</code> if <code>comp</code> is 226 * not contained in a JLayeredPane 227 */ 228 public static JLayeredPane getLayeredPaneAbove(Component comp) 229 { 230 JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass 231 (JLayeredPane.class, comp); 232 return lp; 233 } 234 235 /** 236 * Return the greatest layer number currently in use, in this container. 237 * This number may legally be positive <em>or</em> negative. 238 * 239 * @return the highest layer number 240 * 241 * @see #lowestLayer() 242 */ 243 public int highestLayer() 244 { 245 Component[] components = getComponents(); 246 int highest; 247 if (components.length == 0) 248 highest = 0; 249 else 250 { 251 highest = Integer.MIN_VALUE; 252 for (int i = 0; i < components.length; i++) 253 highest = Math.max(highest, getLayer(components[i])); 254 } 255 return highest; 256 } 257 258 /** 259 * Return the least layer number currently in use, in this container. 260 * This number may legally be positive <em>or</em> negative. 261 * 262 * @return the least layer number 263 * 264 * @see #highestLayer() 265 */ 266 public int lowestLayer() 267 { 268 Component[] components = getComponents(); 269 int lowest; 270 if (components.length == 0) 271 lowest = 0; 272 else 273 { 274 lowest = Integer.MAX_VALUE; 275 for (int i = 0; i < components.length; i++) 276 lowest = Math.max(lowest, getLayer(components[i])); 277 } 278 return lowest; 279 } 280 281 /** 282 * Moves a component to the "front" of its layer. The "front" is a 283 * synonym for position 0, which is also the last position drawn in each 284 * layer, so is usually the component which occludes the most other 285 * components in its layer. 286 * 287 * @param c the component to move to the front of its layer 288 * 289 * @see #moveToBack 290 */ 291 public void moveToFront(Component c) 292 { 293 setPosition (c, 0); 294 } 295 296 /** 297 * <p>Moves a component to the "back" of its layer. The "back" is a 298 * synonym for position N-1 (also known as position -1), where N is the 299 * size of the layer.</p> 300 * 301 * <p>The "back" of a layer is the first position drawn, so the component at 302 * the "back" is usually the component which is occluded by the most 303 * other components in its layer.</p> 304 * 305 * @param c the component to move to the back of its layer. 306 * 307 * @see #moveToFront 308 */ 309 public void moveToBack(Component c) 310 { 311 setPosition (c, -1); 312 } 313 314 /** 315 * Return the position of a component within its layer. Positions are assigned 316 * from the "front" (position 0) to the "back" (position N-1), and drawn from 317 * the back towards the front. 318 * 319 * @param c the component to get the position of 320 * 321 * @return the position of <code>c</code> within its layer or -1 if 322 * <code>c</code> is not a child of this layered pane 323 * 324 * @see #setPosition 325 */ 326 public int getPosition(Component c) 327 { 328 int pos = -1; 329 int index = getIndexOf(c); 330 if (index >= 0) 331 { 332 pos = 0; 333 int layer = getLayer(c); 334 for (int i = index - 1; i >= 0; --i) 335 { 336 if (layer == getLayer(getComponent(i))) 337 pos++; 338 else 339 break; 340 } 341 } 342 return pos; 343 } 344 345 /** 346 * Change the position of a component within its layer. Positions are assigned 347 * from the "front" (position 0) to the "back" (position N-1), and drawn from 348 * the back towards the front. 349 * 350 * @param c the component to change the position of 351 * @param position the position to assign the component to 352 * 353 * @see #getPosition 354 */ 355 public void setPosition(Component c, int position) 356 { 357 setLayer(c, getLayer(c), position); 358 } 359 360 /** 361 * Return an array of all components within a layer of this 362 * container. Components are ordered front-to-back, with the "front" 363 * element (which draws last) at position 0 of the returned array. 364 * 365 * @param layer the layer to return components from 366 * 367 * @return the components in the layer 368 */ 369 public Component[] getComponentsInLayer(int layer) 370 { 371 Component[] inLayer = new Component[getComponentCountInLayer(layer)]; 372 Component[] components = getComponents(); 373 int j = 0; 374 for (int i = 0; i < components.length; ++i) 375 { 376 if (layer == getLayer(components[i])) 377 { 378 inLayer[j] = components[i]; 379 j++; 380 } 381 } 382 return inLayer; 383 } 384 385 /** 386 * Return the number of components within a layer of this 387 * container. 388 * 389 * @param layer the layer count components in 390 * 391 * @return the number of components in the layer 392 */ 393 public int getComponentCountInLayer(int layer) 394 { 395 Component[] components = getComponents(); 396 int count = 0; 397 for (int i = components.length - 1; i >= 0; --i) 398 { 399 if (getLayer(components[i]) == layer) 400 count++; 401 } 402 return count; 403 } 404 405 /** 406 * Return a hashtable mapping child components of this container to 407 * Integer objects representing the component's layer assignments. 408 */ 409 protected Hashtable<Component, Integer> getComponentToLayer() 410 { 411 return componentToLayer; 412 } 413 414 /** 415 * Return the index of a component within the underlying (contiguous) 416 * array of children. This is a "raw" number which does not represent the 417 * child's position in a layer, but rather its position in the logical 418 * drawing order of all children of the container. 419 * 420 * @param c the component to look up. 421 * 422 * @return the external index of the component or <code>-1</code> if 423 * <code>c</code> is not a child of this layered pane 424 */ 425 public int getIndexOf(Component c) 426 { 427 return getComponentZOrder(c); 428 } 429 430 /** 431 * Return an Integer object which holds the same int value as the 432 * parameter. This is strictly an optimization to minimize the number of 433 * identical Integer objects which we allocate. 434 * 435 * @param layer the layer number as an int. 436 * 437 * @return the layer number as an Integer, possibly shared. 438 */ 439 protected Integer getObjectForLayer(int layer) 440 { 441 switch (layer) 442 { 443 case -30000: 444 return FRAME_CONTENT_LAYER; 445 446 case 0: 447 return DEFAULT_LAYER; 448 449 case 100: 450 return PALETTE_LAYER; 451 452 case 200: 453 return MODAL_LAYER; 454 455 case 300: 456 return POPUP_LAYER; 457 458 case 400: 459 return DRAG_LAYER; 460 461 default: 462 break; 463 } 464 465 return new Integer(layer); 466 } 467 468 /** 469 * Computes an index at which to request the superclass {@link 470 * java.awt.Container} inserts a component, given an abstract layer and 471 * position number. 472 * 473 * @param layer the layer in which to insert a component. 474 * @param position the position in the layer at which to insert a component. 475 * 476 * @return the index at which to insert the component. 477 */ 478 protected int insertIndexForLayer(int layer, int position) 479 { 480 return insertIndexForLayer(null, layer, position); 481 } 482 483 /** 484 * Similar to {@link #insertIndexForLayer(int, int)}, only that it takes a 485 * component parameter, which should be ignored in the search. This is 486 * necessary to support {@link #setLayer(Component, int, int)} which uses 487 * Container.setComponentZOrder(), which doesn't remove the component. 488 * 489 * @param comp the component to ignore 490 * @param layer the layer 491 * @param position the position 492 * 493 * @return the insertion index 494 */ 495 private int insertIndexForLayer(Component comp, int layer, int position) 496 { 497 // Create the component list to search through. 498 ArrayList l = new ArrayList(); 499 int count = getComponentCount(); 500 for (int i = 0; i < count; i++) 501 { 502 Component c = getComponent(i); 503 if (c != comp) 504 l.add(c); 505 } 506 507 count = l.size(); 508 int layerStart = -1; 509 int layerEnd = -1; 510 for (int i = 0; i < count; i++) 511 { 512 int layerOfComponent = getLayer((Component) l.get(i)); 513 if (layerStart == -1 && layerOfComponent == layer) 514 layerStart = i; 515 if (layerOfComponent < layer) 516 { 517 // We are beyond the layer that we are looking for. Update the 518 // layerStart and layerEnd and exit the loop. 519 if (i == 0) 520 { 521 layerStart = 0; 522 layerEnd = 0; 523 } 524 else 525 layerEnd = i; 526 break; 527 } 528 } 529 530 // No layer found. The requested layer is lower than any existing layer, 531 // put the component at the end. 532 int insertIndex; 533 if (layerStart == -1 && layerEnd == -1) 534 { 535 insertIndex = count; 536 } 537 else 538 { 539 // Corner cases. 540 if (layerStart != -1 && layerEnd == -1) 541 layerEnd = count; 542 if (layerStart == -1 && layerEnd != -1) 543 layerStart = layerEnd; 544 545 // Adding to the bottom of a layer returns the end index 546 // in the layer. 547 if (position == -1) 548 insertIndex = layerEnd; 549 else 550 { 551 // Insert into a layer. 552 if (position > -1 && layerStart + position <= layerEnd) 553 insertIndex = layerStart + position; 554 else 555 insertIndex = layerEnd; 556 } 557 } 558 return insertIndex; 559 } 560 561 /** 562 * Removes a child from this container. The child is specified by 563 * index. After removal, the child no longer occupies a layer. 564 * 565 * @param index the index of the child component to remove. 566 */ 567 public void remove(int index) 568 { 569 Component c = getComponent(index); 570 if (! (c instanceof JComponent)) 571 componentToLayer.remove(c); 572 super.remove(index); 573 } 574 575 /** 576 * Removes all components from this container. 577 * 578 * @since 1.5 579 */ 580 public void removeAll() 581 { 582 componentToLayer.clear(); 583 super.removeAll(); 584 } 585 586 /** 587 * <p>Set the layer property for a component, within this container. The 588 * component will be implicitly mapped to the bottom-most position in the 589 * layer, but only if added <em>after</em> calling this method.</p> 590 * 591 * <p>Read that carefully: this method should be called <em>before</em> the 592 * component is added to the container.</p> 593 * 594 * @param c the component to set the layer property for. 595 * @param layer the layer number to assign to the component. 596 */ 597 public void setLayer(Component c, int layer) 598 { 599 setLayer(c, layer, -1); 600 } 601 602 /** 603 * Set the layer and position of a component, within this container. 604 * 605 * @param c the child component to set the layer property for. 606 * @param layer the layer number to assign to the component. 607 * @param position the position number to assign to the component. 608 */ 609 public void setLayer(Component c, int layer, int position) 610 { 611 Integer layerObj = getObjectForLayer(layer); 612 613 // Nothing to do if neither the layer nor the position is 614 // changed. 615 if (layer != getLayer(c) || position != getPosition(c)) 616 { 617 // Store the layer either in the JComponent or in the hashtable 618 if (c instanceof JComponent) 619 { 620 JComponent jc = (JComponent) c; 621 jc.putClientProperty(LAYER_PROPERTY, layerObj); 622 } 623 else 624 componentToLayer.put (c, layerObj); 625 626 // Update the component in the Z order of the Container. 627 Container parent = c.getParent(); 628 if (parent == this) 629 { 630 int index = insertIndexForLayer(c, layer, position); 631 setComponentZOrder(c, index); 632 } 633 } 634 repaint(c.getX(), c.getY(), c.getWidth(), c.getHeight()); 635 } 636 637 /** 638 * Overrides the default implementation from {@link java.awt.Container} 639 * such that <code>layerConstraint</code> is interpreted as an {@link 640 * Integer}, specifying the layer to which the component will be added 641 * (at the bottom position). 642 * 643 * The argument <code>index</code> specifies the position within the layer 644 * at which the component should be added, where <code>0</code> is the top 645 * position greater values specify positions below that and <code>-1</code> 646 * specifies the bottom position. 647 * 648 * @param comp the component to add 649 * @param layerConstraint an integer specifying the layer to add the 650 * component to 651 * @param index the position within the layer 652 */ 653 protected void addImpl(Component comp, Object layerConstraint, int index) 654 { 655 int layer; 656 if (layerConstraint != null && layerConstraint instanceof Integer) 657 { 658 layer = ((Integer) layerConstraint).intValue(); 659 setLayer(comp, layer); 660 } 661 else 662 layer = getLayer(comp); 663 664 int newIdx = insertIndexForLayer(layer, index); 665 super.addImpl(comp, layerConstraint, newIdx); 666 comp.validate(); 667 comp.repaint(); 668 } 669 670 /** 671 * Sets the layer property for a JComponent. 672 * 673 * @param component the component for which to set the layer 674 * @param layer the layer property to set 675 */ 676 public static void putLayer(JComponent component, int layer) 677 { 678 component.putClientProperty(LAYER_PROPERTY, new Integer(layer)); 679 } 680 681 /** 682 * Returns the accessible context for this <code>JLayeredPane</code>. 683 * 684 * @return the accessible context for this <code>JLayeredPane</code> 685 */ 686 public AccessibleContext getAccessibleContext() 687 { 688 if (accessibleContext == null) 689 accessibleContext = new AccessibleJLayeredPane(); 690 return accessibleContext; 691 } 692 693 /** 694 * This method is overridden order to provide a reasonable painting 695 * mechanism for <code>JLayeredPane</code>. This is necessary since 696 * <code>JLayeredPane</code>'s do not have an own UI delegate. 697 * 698 * Basically this method clears the background for the 699 * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>. 700 * 701 * @param g the graphics context to use 702 */ 703 public void paint(Graphics g) 704 { 705 if (isOpaque()) 706 { 707 Color oldColor = g.getColor(); 708 Rectangle clip = g.getClipBounds(); 709 g.setColor(getBackground()); 710 g.fillRect(clip.x, clip.y, clip.width, clip.height); 711 g.setColor(oldColor); 712 } 713 super.paint(g); 714 } 715 716 /** 717 * Returns <code>false</code> if components in this layered pane can overlap, 718 * otherwise <code>true</code>. 719 * 720 * @return <code>false</code> if components in this layered pane can overlap, 721 * otherwise <code>true</code> 722 */ 723 public boolean isOptimizedDrawingEnabled() 724 { 725 int numChildren = getComponentCount(); 726 boolean result = true; 727 for (int i = 0; i < numChildren; ++i) 728 { 729 Component c1 = getComponent(i); 730 if (! c1.isVisible()) 731 continue; 732 Rectangle r1 = c1.getBounds(); 733 if (r1.isEmpty()) 734 continue; 735 736 for (int j = i + 1; j < numChildren; ++j) 737 { 738 Component c2 = getComponent(j); 739 if (! c2.isVisible()) 740 continue; 741 Rectangle r2 = c2.getBounds(); 742 if (r2.isEmpty()) 743 continue; 744 if (r1.intersects(r2)) 745 { 746 result = false; 747 break; 748 } 749 if (result == false) 750 break; 751 } 752 } 753 return result; 754 } 755 }