1 /*
   2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 import java.awt.Component;
  28 import java.awt.Image;
  29 import java.awt.*;
  30 import java.text.*;
  31 import java.awt.geom.*;
  32 import java.beans.JavaBean;
  33 import java.beans.BeanProperty;
  34 import java.beans.Transient;
  35 
  36 import java.io.ObjectOutputStream;
  37 import java.io.IOException;
  38 
  39 import javax.swing.plaf.LabelUI;
  40 import javax.accessibility.*;
  41 import javax.swing.text.*;
  42 
  43 /**
  44  * A display area for a short text string or an image,
  45  * or both.
  46  * A label does not react to input events.
  47  * As a result, it cannot get the keyboard focus.
  48  * A label can, however, display a keyboard alternative
  49  * as a convenience for a nearby component
  50  * that has a keyboard alternative but can't display it.
  51  * <p>
  52  * A <code>JLabel</code> object can display
  53  * either text, an image, or both.
  54  * You can specify where in the label's display area
  55  * the label's contents are aligned
  56  * by setting the vertical and horizontal alignment.
  57  * By default, labels are vertically centered
  58  * in their display area.
  59  * Text-only labels are leading edge aligned, by default;
  60  * image-only labels are horizontally centered, by default.
  61  * <p>
  62  * You can also specify the position of the text
  63  * relative to the image.
  64  * By default, text is on the trailing edge of the image,
  65  * with the text and image vertically aligned.
  66  * <p>
  67  * A label's leading and trailing edge are determined from the value of its
  68  * {@link java.awt.ComponentOrientation} property.  At present, the default
  69  * ComponentOrientation setting maps the leading edge to left and the trailing
  70  * edge to right.
  71  *
  72  * <p>
  73  * Finally, you can use the <code>setIconTextGap</code> method
  74  * to specify how many pixels
  75  * should appear between the text and the image.
  76  * The default is 4 pixels.
  77  * <p>
  78  * See <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/label.html">How to Use Labels</a>
  79  * in <em>The Java Tutorial</em>
  80  * for further documentation.
  81  * <p>
  82  * <strong>Warning:</strong> Swing is not thread safe. For more
  83  * information see <a
  84  * href="package-summary.html#threading">Swing's Threading
  85  * Policy</a>.
  86  * <p>
  87  * <strong>Warning:</strong>
  88  * Serialized objects of this class will not be compatible with
  89  * future Swing releases. The current serialization support is
  90  * appropriate for short term storage or RMI between applications running
  91  * the same version of Swing.  As of 1.4, support for long term storage
  92  * of all JavaBeans&trade;
  93  * has been added to the <code>java.beans</code> package.
  94  * Please see {@link java.beans.XMLEncoder}.
  95  *
  96  * @author Hans Muller
  97  * @since 1.2
  98  */
  99 @JavaBean(defaultProperty = "UI", description = "A component that displays a short string and an icon.")
 100 @SwingContainer(false)
 101 @SuppressWarnings("serial")
 102 public class JLabel extends JComponent implements SwingConstants, Accessible
 103 {
 104     /**
 105      * @see #getUIClassID
 106      * @see #readObject
 107      */
 108     private static final String uiClassID = "LabelUI";
 109 
 110     private int mnemonic = '\0';
 111     private int mnemonicIndex = -1;
 112 
 113     private String text = "";         // "" rather than null, for BeanBox
 114     private Icon defaultIcon = null;
 115     private Icon disabledIcon = null;
 116     private boolean disabledIconSet = false;
 117 
 118     private int verticalAlignment = CENTER;
 119     private int horizontalAlignment = LEADING;
 120     private int verticalTextPosition = CENTER;
 121     private int horizontalTextPosition = TRAILING;
 122     private int iconTextGap = 4;
 123 
 124     /**
 125      * The Component this label is for; null if the label
 126      * is not the label for a component
 127      */
 128     protected Component labelFor = null;
 129 
 130     /**
 131      * Client property key used to determine what label is labeling the
 132      * component.  This is generally not used by labels, but is instead
 133      * used by components such as text areas that are being labeled by
 134      * labels.  When the labelFor property of a label is set, it will
 135      * automatically set the LABELED_BY_PROPERTY of the component being
 136      * labelled.
 137      *
 138      * @see #setLabelFor
 139      */
 140     static final String LABELED_BY_PROPERTY = "labeledBy";
 141 
 142     /**
 143      * Creates a <code>JLabel</code> instance with the specified
 144      * text, image, and horizontal alignment.
 145      * The label is centered vertically in its display area.
 146      * The text is on the trailing edge of the image.
 147      *
 148      * @param text  The text to be displayed by the label.
 149      * @param icon  The image to be displayed by the label.
 150      * @param horizontalAlignment  One of the following constants
 151      *           defined in <code>SwingConstants</code>:
 152      *           <code>LEFT</code>,
 153      *           <code>CENTER</code>,
 154      *           <code>RIGHT</code>,
 155      *           <code>LEADING</code> or
 156      *           <code>TRAILING</code>.
 157      */
 158     public JLabel(String text, Icon icon, int horizontalAlignment) {
 159         setText(text);
 160         setIcon(icon);
 161         setHorizontalAlignment(horizontalAlignment);
 162         updateUI();
 163         setAlignmentX(LEFT_ALIGNMENT);
 164     }
 165 
 166     /**
 167      * Creates a <code>JLabel</code> instance with the specified
 168      * text and horizontal alignment.
 169      * The label is centered vertically in its display area.
 170      *
 171      * @param text  The text to be displayed by the label.
 172      * @param horizontalAlignment  One of the following constants
 173      *           defined in <code>SwingConstants</code>:
 174      *           <code>LEFT</code>,
 175      *           <code>CENTER</code>,
 176      *           <code>RIGHT</code>,
 177      *           <code>LEADING</code> or
 178      *           <code>TRAILING</code>.
 179      */
 180     public JLabel(String text, int horizontalAlignment) {
 181         this(text, null, horizontalAlignment);
 182     }
 183 
 184     /**
 185      * Creates a <code>JLabel</code> instance with the specified text.
 186      * The label is aligned against the leading edge of its display area,
 187      * and centered vertically.
 188      *
 189      * @param text  The text to be displayed by the label.
 190      */
 191     public JLabel(String text) {
 192         this(text, null, LEADING);
 193     }
 194 
 195     /**
 196      * Creates a <code>JLabel</code> instance with the specified
 197      * image and horizontal alignment.
 198      * The label is centered vertically in its display area.
 199      *
 200      * @param image  The image to be displayed by the label.
 201      * @param horizontalAlignment  One of the following constants
 202      *           defined in <code>SwingConstants</code>:
 203      *           <code>LEFT</code>,
 204      *           <code>CENTER</code>,
 205      *           <code>RIGHT</code>,
 206      *           <code>LEADING</code> or
 207      *           <code>TRAILING</code>.
 208      */
 209     public JLabel(Icon image, int horizontalAlignment) {
 210         this(null, image, horizontalAlignment);
 211     }
 212 
 213     /**
 214      * Creates a <code>JLabel</code> instance with the specified image.
 215      * The label is centered vertically and horizontally
 216      * in its display area.
 217      *
 218      * @param image  The image to be displayed by the label.
 219      */
 220     public JLabel(Icon image) {
 221         this(null, image, CENTER);
 222     }
 223 
 224     /**
 225      * Creates a <code>JLabel</code> instance with
 226      * no image and with an empty string for the title.
 227      * The label is centered vertically
 228      * in its display area.
 229      * The label's contents, once set, will be displayed on the leading edge
 230      * of the label's display area.
 231      */
 232     public JLabel() {
 233         this("", null, LEADING);
 234     }
 235 
 236 
 237     /**
 238      * Returns the L&amp;F object that renders this component.
 239      *
 240      * @return LabelUI object
 241      */
 242     public LabelUI getUI() {
 243         return (LabelUI)ui;
 244     }
 245 
 246 
 247     /**
 248      * Sets the L&amp;F object that renders this component.
 249      *
 250      * @param ui  the LabelUI L&amp;F object
 251      * @see UIDefaults#getUI
 252      */
 253     @BeanProperty(hidden = true, visualUpdate = true, description
 254             = "The UI object that implements the Component's LookAndFeel.")
 255     public void setUI(LabelUI ui) {
 256         super.setUI(ui);
 257         // disabled icon is generated by LF so it should be unset here
 258         if (!disabledIconSet && disabledIcon != null) {
 259             setDisabledIcon(null);
 260         }
 261     }
 262 
 263 
 264     /**
 265      * Resets the UI property to a value from the current look and feel.
 266      *
 267      * @see JComponent#updateUI
 268      */
 269     public void updateUI() {
 270         setUI((LabelUI) UIManager.getUI(this));
 271     }
 272 
 273 
 274     /**
 275      * Returns a string that specifies the name of the l&amp;f class
 276      * that renders this component.
 277      *
 278      * @return String "LabelUI"
 279      *
 280      * @see JComponent#getUIClassID
 281      * @see UIDefaults#getUI
 282      */
 283     @BeanProperty(bound = false)
 284     public String getUIClassID() {
 285         return uiClassID;
 286     }
 287 
 288 
 289     /**
 290      * Returns the text string that the label displays.
 291      *
 292      * @return a String
 293      * @see #setText
 294      */
 295     public String getText() {
 296         return text;
 297     }
 298 
 299 
 300     /**
 301      * Defines the single line of text this component will display.  If
 302      * the value of text is null or empty string, nothing is displayed.
 303      * <p>
 304      * The default value of this property is null.
 305      * <p>
 306      * This is a JavaBeans bound property.
 307      *
 308      * @param text  the single line of text this component will display
 309      * @see #setVerticalTextPosition
 310      * @see #setHorizontalTextPosition
 311      * @see #setIcon
 312      */
 313     @BeanProperty(preferred = true, visualUpdate = true, description
 314             = "Defines the single line of text this component will display.")
 315     public void setText(String text) {
 316 
 317         String oldAccessibleName = null;
 318         if (accessibleContext != null) {
 319             oldAccessibleName = accessibleContext.getAccessibleName();
 320         }
 321 
 322         String oldValue = this.text;
 323         this.text = text;
 324         firePropertyChange("text", oldValue, text);
 325 
 326         setDisplayedMnemonicIndex(
 327                       SwingUtilities.findDisplayedMnemonicIndex(
 328                                           text, getDisplayedMnemonic()));
 329 
 330         if ((accessibleContext != null)
 331             && (accessibleContext.getAccessibleName() != oldAccessibleName)) {
 332                 accessibleContext.firePropertyChange(
 333                         AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
 334                         oldAccessibleName,
 335                         accessibleContext.getAccessibleName());
 336         }
 337         if (text == null || oldValue == null || !text.equals(oldValue)) {
 338             revalidate();
 339             repaint();
 340         }
 341     }
 342 
 343 
 344     /**
 345      * Returns the graphic image (glyph, icon) that the label displays.
 346      *
 347      * @return an Icon
 348      * @see #setIcon
 349      */
 350     public Icon getIcon() {
 351         return defaultIcon;
 352     }
 353 
 354     /**
 355      * Defines the icon this component will display.  If
 356      * the value of icon is null, nothing is displayed.
 357      * <p>
 358      * The default value of this property is null.
 359      * <p>
 360      * This is a JavaBeans bound property.
 361      *
 362      * @param icon  the default icon this component will display
 363      * @see #setVerticalTextPosition
 364      * @see #setHorizontalTextPosition
 365      * @see #getIcon
 366      */
 367     @BeanProperty(preferred = true, visualUpdate = true, description
 368             = "The icon this component will display.")
 369     public void setIcon(Icon icon) {
 370         Icon oldValue = defaultIcon;
 371         defaultIcon = icon;
 372 
 373         /* If the default icon has really changed and we had
 374          * generated the disabled icon for this component
 375          * (in other words, setDisabledIcon() was never called), then
 376          * clear the disabledIcon field.
 377          */
 378         if ((defaultIcon != oldValue) && !disabledIconSet) {
 379             disabledIcon = null;
 380         }
 381 
 382         firePropertyChange("icon", oldValue, defaultIcon);
 383 
 384         if ((accessibleContext != null) && (oldValue != defaultIcon)) {
 385                 accessibleContext.firePropertyChange(
 386                         AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
 387                         oldValue, defaultIcon);
 388         }
 389 
 390         /* If the default icon has changed and the new one is
 391          * a different size, then revalidate.   Repaint if the
 392          * default icon has changed.
 393          */
 394         if (defaultIcon != oldValue) {
 395             if ((defaultIcon == null) ||
 396                 (oldValue == null) ||
 397                 (defaultIcon.getIconWidth() != oldValue.getIconWidth()) ||
 398                 (defaultIcon.getIconHeight() != oldValue.getIconHeight())) {
 399                 revalidate();
 400             }
 401             repaint();
 402         }
 403     }
 404 
 405 
 406     /**
 407      * Returns the icon used by the label when it's disabled.
 408      * If no disabled icon has been set this will forward the call to
 409      * the look and feel to construct an appropriate disabled Icon.
 410      * <p>
 411      * Some look and feels might not render the disabled Icon, in which
 412      * case they will ignore this.
 413      *
 414      * @return the <code>disabledIcon</code> property
 415      * @see #setDisabledIcon
 416      * @see javax.swing.LookAndFeel#getDisabledIcon
 417      * @see ImageIcon
 418      */
 419     @Transient
 420     public Icon getDisabledIcon() {
 421         if (!disabledIconSet && disabledIcon == null && defaultIcon != null) {
 422             disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, defaultIcon);
 423             if (disabledIcon != null) {
 424                 firePropertyChange("disabledIcon", null, disabledIcon);
 425             }
 426         }
 427         return disabledIcon;
 428     }
 429 
 430 
 431     /**
 432      * Set the icon to be displayed if this JLabel is "disabled"
 433      * (JLabel.setEnabled(false)).
 434      * <p>
 435      * The default value of this property is null.
 436      *
 437      * @param disabledIcon the Icon to display when the component is disabled
 438      * @see #getDisabledIcon
 439      * @see #setEnabled
 440      */
 441     @BeanProperty(visualUpdate = true, description
 442             = "The icon to display if the label is disabled.")
 443     public void setDisabledIcon(Icon disabledIcon) {
 444         Icon oldValue = this.disabledIcon;
 445         this.disabledIcon = disabledIcon;
 446         disabledIconSet = (disabledIcon != null);
 447         firePropertyChange("disabledIcon", oldValue, disabledIcon);
 448         if (disabledIcon != oldValue) {
 449             if (disabledIcon == null || oldValue == null ||
 450                 disabledIcon.getIconWidth() != oldValue.getIconWidth() ||
 451                 disabledIcon.getIconHeight() != oldValue.getIconHeight()) {
 452                 revalidate();
 453             }
 454             if (!isEnabled()) {
 455                 repaint();
 456             }
 457         }
 458     }
 459 
 460 
 461     /**
 462      * Specify a keycode that indicates a mnemonic key.
 463      * This property is used when the label is part of a larger component.
 464      * If the labelFor property of the label is not null, the label will
 465      * call the requestFocus method of the component specified by the
 466      * labelFor property when the mnemonic is activated.
 467      *
 468      * @param key  a keycode that indicates a mnemonic key
 469      * @see #getLabelFor
 470      * @see #setLabelFor
 471      */
 472     @BeanProperty(visualUpdate = true, description
 473             = "The mnemonic keycode.")
 474     public void setDisplayedMnemonic(int key) {
 475         int oldKey = mnemonic;
 476         mnemonic = key;
 477         firePropertyChange("displayedMnemonic", oldKey, mnemonic);
 478 
 479         setDisplayedMnemonicIndex(
 480             SwingUtilities.findDisplayedMnemonicIndex(getText(), mnemonic));
 481 
 482         if (key != oldKey) {
 483             revalidate();
 484             repaint();
 485         }
 486     }
 487 
 488 
 489     /**
 490      * Specifies the displayedMnemonic as a char value.
 491      *
 492      * @param aChar  a char specifying the mnemonic to display
 493      * @see #setDisplayedMnemonic(int)
 494      */
 495     public void setDisplayedMnemonic(char aChar) {
 496         int vk = java.awt.event.KeyEvent.getExtendedKeyCodeForChar(aChar);
 497         if (vk != java.awt.event.KeyEvent.VK_UNDEFINED) {
 498             setDisplayedMnemonic(vk);
 499         }
 500     }
 501 
 502 
 503     /**
 504      * Return the keycode that indicates a mnemonic key.
 505      * This property is used when the label is part of a larger component.
 506      * If the labelFor property of the label is not null, the label will
 507      * call the requestFocus method of the component specified by the
 508      * labelFor property when the mnemonic is activated.
 509      *
 510      * @return int value for the mnemonic key
 511      *
 512      * @see #getLabelFor
 513      * @see #setLabelFor
 514      */
 515     public int getDisplayedMnemonic() {
 516         return mnemonic;
 517     }
 518 
 519     /**
 520      * Provides a hint to the look and feel as to which character in the
 521      * text should be decorated to represent the mnemonic. Not all look and
 522      * feels may support this. A value of -1 indicates either there is no
 523      * mnemonic, the mnemonic character is not contained in the string, or
 524      * the developer does not wish the mnemonic to be displayed.
 525      * <p>
 526      * The value of this is updated as the properties relating to the
 527      * mnemonic change (such as the mnemonic itself, the text...).
 528      * You should only ever have to call this if
 529      * you do not wish the default character to be underlined. For example, if
 530      * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
 531      * to be decorated, as 'Save <u>A</u>s', you would have to invoke
 532      * <code>setDisplayedMnemonicIndex(5)</code> after invoking
 533      * <code>setDisplayedMnemonic(KeyEvent.VK_A)</code>.
 534      *
 535      * @since 1.4
 536      * @param index Index into the String to underline
 537      * @exception IllegalArgumentException will be thrown if <code>index</code>
 538      *            is &gt;= length of the text, or &lt; -1
 539      */
 540     @BeanProperty(visualUpdate = true, description
 541             = "the index into the String to draw the keyboard character mnemonic at")
 542     public void setDisplayedMnemonicIndex(int index)
 543                                              throws IllegalArgumentException {
 544         int oldValue = mnemonicIndex;
 545         if (index == -1) {
 546             mnemonicIndex = -1;
 547         } else {
 548             String text = getText();
 549             int textLength = (text == null) ? 0 : text.length();
 550             if (index < -1 || index >= textLength) {  // index out of range
 551                 throw new IllegalArgumentException("index == " + index);
 552             }
 553         }
 554         mnemonicIndex = index;
 555         firePropertyChange("displayedMnemonicIndex", oldValue, index);
 556         if (index != oldValue) {
 557             revalidate();
 558             repaint();
 559         }
 560     }
 561 
 562     /**
 563      * Returns the character, as an index, that the look and feel should
 564      * provide decoration for as representing the mnemonic character.
 565      *
 566      * @since 1.4
 567      * @return index representing mnemonic character
 568      * @see #setDisplayedMnemonicIndex
 569      */
 570     public int getDisplayedMnemonicIndex() {
 571         return mnemonicIndex;
 572     }
 573 
 574     /**
 575      * Verify that key is a legal value for the horizontalAlignment properties.
 576      *
 577      * @param key the property value to check
 578      * @param message the IllegalArgumentException detail message
 579      * @return the key value if {@code key} is a a legal value for the
 580      *         horizontalAlignment properties
 581      * @exception IllegalArgumentException if key isn't LEFT, CENTER, RIGHT,
 582      * LEADING or TRAILING.
 583      * @see #setHorizontalTextPosition
 584      * @see #setHorizontalAlignment
 585      */
 586     protected int checkHorizontalKey(int key, String message) {
 587         if ((key == LEFT) ||
 588             (key == CENTER) ||
 589             (key == RIGHT) ||
 590             (key == LEADING) ||
 591             (key == TRAILING)) {
 592             return key;
 593         }
 594         else {
 595             throw new IllegalArgumentException(message);
 596         }
 597     }
 598 
 599 
 600     /**
 601      * Verify that key is a legal value for the
 602      * verticalAlignment or verticalTextPosition properties.
 603      *
 604      * @param key the property value to check
 605      * @param message the IllegalArgumentException detail message
 606      * @return the key value if {@code key} is a legal value for the
 607      *         verticalAlignment or verticalTextPosition properties
 608      * @exception IllegalArgumentException if key isn't TOP, CENTER, or BOTTOM.
 609      * @see #setVerticalAlignment
 610      * @see #setVerticalTextPosition
 611      */
 612     protected int checkVerticalKey(int key, String message) {
 613         if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {
 614             return key;
 615         }
 616         else {
 617             throw new IllegalArgumentException(message);
 618         }
 619     }
 620 
 621 
 622     /**
 623      * Returns the amount of space between the text and the icon
 624      * displayed in this label.
 625      *
 626      * @return an int equal to the number of pixels between the text
 627      *         and the icon.
 628      * @see #setIconTextGap
 629      */
 630     public int getIconTextGap() {
 631         return iconTextGap;
 632     }
 633 
 634 
 635     /**
 636      * If both the icon and text properties are set, this property
 637      * defines the space between them.
 638      * <p>
 639      * The default value of this property is 4 pixels.
 640      * <p>
 641      * This is a JavaBeans bound property.
 642      *
 643      * @param iconTextGap  the space between the icon and text properties
 644      * @see #getIconTextGap
 645      */
 646     @BeanProperty(visualUpdate = true, description
 647             = "If both the icon and text properties are set, this property defines the space between them.")
 648     public void setIconTextGap(int iconTextGap) {
 649         int oldValue = this.iconTextGap;
 650         this.iconTextGap = iconTextGap;
 651         firePropertyChange("iconTextGap", oldValue, iconTextGap);
 652         if (iconTextGap != oldValue) {
 653             revalidate();
 654             repaint();
 655         }
 656     }
 657 
 658 
 659 
 660     /**
 661      * Returns the alignment of the label's contents along the Y axis.
 662      *
 663      * @return   The value of the verticalAlignment property, one of the
 664      *           following constants defined in <code>SwingConstants</code>:
 665      *           <code>TOP</code>,
 666      *           <code>CENTER</code>, or
 667      *           <code>BOTTOM</code>.
 668      *
 669      * @see SwingConstants
 670      * @see #setVerticalAlignment
 671      */
 672     public int getVerticalAlignment() {
 673         return verticalAlignment;
 674     }
 675 
 676 
 677     /**
 678      * Sets the alignment of the label's contents along the Y axis.
 679      * <p>
 680      * The default value of this property is CENTER.
 681      *
 682      * @param alignment One of the following constants
 683      *           defined in <code>SwingConstants</code>:
 684      *           <code>TOP</code>,
 685      *           <code>CENTER</code> (the default), or
 686      *           <code>BOTTOM</code>.
 687      *
 688      * @see SwingConstants
 689      * @see #getVerticalAlignment
 690      */
 691     @BeanProperty(visualUpdate = true, enumerationValues = {
 692             "SwingConstants.TOP",
 693             "SwingConstants.CENTER",
 694             "SwingConstants.BOTTOM"},
 695             description = "The alignment of the label's contents along the Y axis.")
 696     public void setVerticalAlignment(int alignment) {
 697         if (alignment == verticalAlignment) return;
 698         int oldValue = verticalAlignment;
 699         verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
 700         firePropertyChange("verticalAlignment", oldValue, verticalAlignment);
 701         repaint();
 702     }
 703 
 704 
 705     /**
 706      * Returns the alignment of the label's contents along the X axis.
 707      *
 708      * @return   The value of the horizontalAlignment property, one of the
 709      *           following constants defined in <code>SwingConstants</code>:
 710      *           <code>LEFT</code>,
 711      *           <code>CENTER</code>,
 712      *           <code>RIGHT</code>,
 713      *           <code>LEADING</code> or
 714      *           <code>TRAILING</code>.
 715      *
 716      * @see #setHorizontalAlignment
 717      * @see SwingConstants
 718      */
 719     public int getHorizontalAlignment() {
 720         return horizontalAlignment;
 721     }
 722 
 723     /**
 724      * Sets the alignment of the label's contents along the X axis.
 725      * <p>
 726      * This is a JavaBeans bound property.
 727      *
 728      * @param alignment  One of the following constants
 729      *           defined in <code>SwingConstants</code>:
 730      *           <code>LEFT</code>,
 731      *           <code>CENTER</code> (the default for image-only labels),
 732      *           <code>RIGHT</code>,
 733      *           <code>LEADING</code> (the default for text-only labels) or
 734      *           <code>TRAILING</code>.
 735      *
 736      * @see SwingConstants
 737      * @see #getHorizontalAlignment
 738      */
 739     @BeanProperty(visualUpdate = true, enumerationValues = {
 740             "SwingConstants.LEFT",
 741             "SwingConstants.CENTER",
 742             "SwingConstants.RIGHT",
 743             "SwingConstants.LEADING",
 744             "SwingConstants.TRAILING"}, description
 745             = "The alignment of the label's content along the X axis.")
 746     public void setHorizontalAlignment(int alignment) {
 747         if (alignment == horizontalAlignment) return;
 748         int oldValue = horizontalAlignment;
 749         horizontalAlignment = checkHorizontalKey(alignment,
 750                                                  "horizontalAlignment");
 751         firePropertyChange("horizontalAlignment",
 752                            oldValue, horizontalAlignment);
 753         repaint();
 754     }
 755 
 756 
 757     /**
 758      * Returns the vertical position of the label's text,
 759      * relative to its image.
 760      *
 761      * @return   One of the following constants
 762      *           defined in <code>SwingConstants</code>:
 763      *           <code>TOP</code>,
 764      *           <code>CENTER</code>, or
 765      *           <code>BOTTOM</code>.
 766      *
 767      * @see #setVerticalTextPosition
 768      * @see SwingConstants
 769      */
 770     public int getVerticalTextPosition() {
 771         return verticalTextPosition;
 772     }
 773 
 774 
 775     /**
 776      * Sets the vertical position of the label's text,
 777      * relative to its image.
 778      * <p>
 779      * The default value of this property is CENTER.
 780      * <p>
 781      * This is a JavaBeans bound property.
 782      *
 783      * @param textPosition  One of the following constants
 784      *           defined in <code>SwingConstants</code>:
 785      *           <code>TOP</code>,
 786      *           <code>CENTER</code> (the default), or
 787      *           <code>BOTTOM</code>.
 788      *
 789      * @see SwingConstants
 790      * @see #getVerticalTextPosition
 791      */
 792     @BeanProperty(expert = true, visualUpdate = true, enumerationValues = {
 793             "SwingConstants.TOP",
 794             "SwingConstants.CENTER",
 795             "SwingConstants.BOTTOM"},
 796             description = "The vertical position of the text relative to it's image.")
 797     public void setVerticalTextPosition(int textPosition) {
 798         if (textPosition == verticalTextPosition) return;
 799         int old = verticalTextPosition;
 800         verticalTextPosition = checkVerticalKey(textPosition,
 801                                                 "verticalTextPosition");
 802         firePropertyChange("verticalTextPosition", old, verticalTextPosition);
 803         revalidate();
 804         repaint();
 805     }
 806 
 807 
 808     /**
 809      * Returns the horizontal position of the label's text,
 810      * relative to its image.
 811      *
 812      * @return   One of the following constants
 813      *           defined in <code>SwingConstants</code>:
 814      *           <code>LEFT</code>,
 815      *           <code>CENTER</code>,
 816      *           <code>RIGHT</code>,
 817      *           <code>LEADING</code> or
 818      *           <code>TRAILING</code>.
 819      *
 820      * @see SwingConstants
 821      */
 822     public int getHorizontalTextPosition() {
 823         return horizontalTextPosition;
 824     }
 825 
 826 
 827     /**
 828      * Sets the horizontal position of the label's text,
 829      * relative to its image.
 830      *
 831      * @param textPosition  One of the following constants
 832      *           defined in <code>SwingConstants</code>:
 833      *           <code>LEFT</code>,
 834      *           <code>CENTER</code>,
 835      *           <code>RIGHT</code>,
 836      *           <code>LEADING</code>, or
 837      *           <code>TRAILING</code> (the default).
 838      *
 839      * @see SwingConstants
 840      */
 841     @BeanProperty(expert = true, visualUpdate = true, enumerationValues = {
 842             "SwingConstants.LEFT",
 843             "SwingConstants.CENTER",
 844             "SwingConstants.RIGHT",
 845             "SwingConstants.LEADING",
 846             "SwingConstants.TRAILING"}, description
 847             = "The horizontal position of the label's text, relative to its image.")
 848     public void setHorizontalTextPosition(int textPosition) {
 849         int old = horizontalTextPosition;
 850         this.horizontalTextPosition = checkHorizontalKey(textPosition,
 851                                                 "horizontalTextPosition");
 852         firePropertyChange("horizontalTextPosition",
 853                            old, horizontalTextPosition);
 854         revalidate();
 855         repaint();
 856     }
 857 
 858 
 859     /**
 860      * This is overridden to return false if the current Icon's Image is
 861      * not equal to the passed in Image <code>img</code>.
 862      *
 863      * @see     java.awt.image.ImageObserver
 864      * @see     java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
 865      */
 866     public boolean imageUpdate(Image img, int infoflags,
 867                                int x, int y, int w, int h) {
 868         // Don't use getDisabledIcon, will trigger creation of icon if icon
 869         // not set.
 870         if (!isShowing() ||
 871             !SwingUtilities.doesIconReferenceImage(getIcon(), img) &&
 872             !SwingUtilities.doesIconReferenceImage(disabledIcon, img)) {
 873 
 874             return false;
 875         }
 876         return super.imageUpdate(img, infoflags, x, y, w, h);
 877     }
 878 
 879 
 880     /**
 881      * See readObject() and writeObject() in JComponent for more
 882      * information about serialization in Swing.
 883      */
 884     private void writeObject(ObjectOutputStream s) throws IOException {
 885         s.defaultWriteObject();
 886         if (getUIClassID().equals(uiClassID)) {
 887             byte count = JComponent.getWriteObjCounter(this);
 888             JComponent.setWriteObjCounter(this, --count);
 889             if (count == 0 && ui != null) {
 890                 ui.installUI(this);
 891             }
 892         }
 893     }
 894 
 895 
 896     /**
 897      * Returns a string representation of this JLabel. This method
 898      * is intended to be used only for debugging purposes, and the
 899      * content and format of the returned string may vary between
 900      * implementations. The returned string may be empty but may not
 901      * be <code>null</code>.
 902      *
 903      * @return  a string representation of this JLabel.
 904      */
 905     protected String paramString() {
 906         String textString = (text != null ?
 907                              text : "");
 908         String defaultIconString = ((defaultIcon != null)
 909                                     && (defaultIcon != this)  ?
 910                                     defaultIcon.toString() : "");
 911         String disabledIconString = ((disabledIcon != null)
 912                                      && (disabledIcon != this) ?
 913                                      disabledIcon.toString() : "");
 914         String labelForString = (labelFor  != null ?
 915                                  labelFor.toString() : "");
 916         String verticalAlignmentString;
 917         if (verticalAlignment == TOP) {
 918             verticalAlignmentString = "TOP";
 919         } else if (verticalAlignment == CENTER) {
 920             verticalAlignmentString = "CENTER";
 921         } else if (verticalAlignment == BOTTOM) {
 922             verticalAlignmentString = "BOTTOM";
 923         } else verticalAlignmentString = "";
 924         String horizontalAlignmentString;
 925         if (horizontalAlignment == LEFT) {
 926             horizontalAlignmentString = "LEFT";
 927         } else if (horizontalAlignment == CENTER) {
 928             horizontalAlignmentString = "CENTER";
 929         } else if (horizontalAlignment == RIGHT) {
 930             horizontalAlignmentString = "RIGHT";
 931         } else if (horizontalAlignment == LEADING) {
 932             horizontalAlignmentString = "LEADING";
 933         } else if (horizontalAlignment == TRAILING) {
 934             horizontalAlignmentString = "TRAILING";
 935         } else horizontalAlignmentString = "";
 936         String verticalTextPositionString;
 937         if (verticalTextPosition == TOP) {
 938             verticalTextPositionString = "TOP";
 939         } else if (verticalTextPosition == CENTER) {
 940             verticalTextPositionString = "CENTER";
 941         } else if (verticalTextPosition == BOTTOM) {
 942             verticalTextPositionString = "BOTTOM";
 943         } else verticalTextPositionString = "";
 944         String horizontalTextPositionString;
 945         if (horizontalTextPosition == LEFT) {
 946             horizontalTextPositionString = "LEFT";
 947         } else if (horizontalTextPosition == CENTER) {
 948             horizontalTextPositionString = "CENTER";
 949         } else if (horizontalTextPosition == RIGHT) {
 950             horizontalTextPositionString = "RIGHT";
 951         } else if (horizontalTextPosition == LEADING) {
 952             horizontalTextPositionString = "LEADING";
 953         } else if (horizontalTextPosition == TRAILING) {
 954             horizontalTextPositionString = "TRAILING";
 955         } else horizontalTextPositionString = "";
 956 
 957         return super.paramString() +
 958         ",defaultIcon=" + defaultIconString +
 959         ",disabledIcon=" + disabledIconString +
 960         ",horizontalAlignment=" + horizontalAlignmentString +
 961         ",horizontalTextPosition=" + horizontalTextPositionString +
 962         ",iconTextGap=" + iconTextGap +
 963         ",labelFor=" + labelForString +
 964         ",text=" + textString +
 965         ",verticalAlignment=" + verticalAlignmentString +
 966         ",verticalTextPosition=" + verticalTextPositionString;
 967     }
 968 
 969     /**
 970      * --- Accessibility Support ---
 971      */
 972 
 973     /**
 974      * Get the component this is labelling.
 975      *
 976      * @return the Component this is labelling.  Can be null if this
 977      * does not label a Component.  If the displayedMnemonic
 978      * property is set and the labelFor property is also set, the label
 979      * will call the requestFocus method of the component specified by the
 980      * labelFor property when the mnemonic is activated.
 981      *
 982      * @see #getDisplayedMnemonic
 983      * @see #setDisplayedMnemonic
 984      */
 985     public Component getLabelFor() {
 986         return labelFor;
 987     }
 988 
 989     /**
 990      * Set the component this is labelling.  Can be null if this does not
 991      * label a Component.  If the displayedMnemonic property is set
 992      * and the labelFor property is also set, the label will
 993      * call the requestFocus method of the component specified by the
 994      * labelFor property when the mnemonic is activated.
 995      *
 996      * @param c  the Component this label is for, or null if the label is
 997      *           not the label for a component
 998      *
 999      * @see #getDisplayedMnemonic
1000      * @see #setDisplayedMnemonic
1001      */
1002     @BeanProperty(description
1003             = "The component this is labelling.")
1004     public void setLabelFor(Component c) {
1005         Component oldC = labelFor;
1006         labelFor = c;
1007         firePropertyChange("labelFor", oldC, c);
1008 
1009         if (oldC instanceof JComponent) {
1010             ((JComponent)oldC).putClientProperty(LABELED_BY_PROPERTY, null);
1011         }
1012         if (c instanceof JComponent) {
1013             ((JComponent)c).putClientProperty(LABELED_BY_PROPERTY, this);
1014         }
1015     }
1016 
1017     /**
1018      * Get the AccessibleContext of this object
1019      *
1020      * @return the AccessibleContext of this object
1021      */
1022     @BeanProperty(bound = false, expert = true, description
1023             = "The AccessibleContext associated with this Label.")
1024     public AccessibleContext getAccessibleContext() {
1025         if (accessibleContext == null) {
1026             accessibleContext = new AccessibleJLabel();
1027         }
1028         return accessibleContext;
1029     }
1030 
1031     /**
1032      * The class used to obtain the accessible role for this object.
1033      * <p>
1034      * <strong>Warning:</strong>
1035      * Serialized objects of this class will not be compatible with
1036      * future Swing releases. The current serialization support is
1037      * appropriate for short term storage or RMI between applications running
1038      * the same version of Swing.  As of 1.4, support for long term storage
1039      * of all JavaBeans&trade;
1040      * has been added to the <code>java.beans</code> package.
1041      * Please see {@link java.beans.XMLEncoder}.
1042      */
1043     @SuppressWarnings("serial")
1044     protected class AccessibleJLabel extends AccessibleJComponent
1045         implements AccessibleText, AccessibleExtendedComponent {
1046 
1047         /**
1048          * Get the accessible name of this object.
1049          *
1050          * @return the localized name of the object -- can be null if this
1051          * object does not have a name
1052          * @see AccessibleContext#setAccessibleName
1053          */
1054         public String getAccessibleName() {
1055             String name = accessibleName;
1056 
1057             if (name == null) {
1058                 name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
1059             }
1060             if (name == null) {
1061                 name = JLabel.this.getText();
1062             }
1063             if (name == null) {
1064                 name = super.getAccessibleName();
1065             }
1066             return name;
1067         }
1068 
1069         /**
1070          * Get the role of this object.
1071          *
1072          * @return an instance of AccessibleRole describing the role of the
1073          * object
1074          * @see AccessibleRole
1075          */
1076         public AccessibleRole getAccessibleRole() {
1077             return AccessibleRole.LABEL;
1078         }
1079 
1080         /**
1081          * Get the AccessibleIcons associated with this object if one
1082          * or more exist.  Otherwise return null.
1083          * @since 1.3
1084          */
1085         public AccessibleIcon [] getAccessibleIcon() {
1086             Icon icon = getIcon();
1087             if (icon instanceof Accessible) {
1088                 AccessibleContext ac =
1089                 ((Accessible)icon).getAccessibleContext();
1090                 if (ac != null && ac instanceof AccessibleIcon) {
1091                     return new AccessibleIcon[] { (AccessibleIcon)ac };
1092                 }
1093             }
1094             return null;
1095         }
1096 
1097         /**
1098          * Get the AccessibleRelationSet associated with this object if one
1099          * exists.  Otherwise return null.
1100          * @see AccessibleRelation
1101          * @since 1.3
1102          */
1103         public AccessibleRelationSet getAccessibleRelationSet() {
1104             // Check where the AccessibleContext's relation
1105             // set already contains a LABEL_FOR relation.
1106             AccessibleRelationSet relationSet
1107                 = super.getAccessibleRelationSet();
1108 
1109             if (!relationSet.contains(AccessibleRelation.LABEL_FOR)) {
1110                 Component c = JLabel.this.getLabelFor();
1111                 if (c != null) {
1112                     AccessibleRelation relation
1113                         = new AccessibleRelation(AccessibleRelation.LABEL_FOR);
1114                     relation.setTarget(c);
1115                     relationSet.add(relation);
1116                 }
1117             }
1118             return relationSet;
1119         }
1120 
1121 
1122         /* AccessibleText ---------- */
1123 
1124         public AccessibleText getAccessibleText() {
1125             View view = (View)JLabel.this.getClientProperty("html");
1126             if (view != null) {
1127                 return this;
1128             } else {
1129                 return null;
1130             }
1131         }
1132 
1133         /**
1134          * Given a point in local coordinates, return the zero-based index
1135          * of the character under that Point.  If the point is invalid,
1136          * this method returns -1.
1137          *
1138          * @param p the Point in local coordinates
1139          * @return the zero-based index of the character under Point p; if
1140          * Point is invalid returns -1.
1141          * @since 1.3
1142          */
1143         public int getIndexAtPoint(Point p) {
1144             View view = (View) JLabel.this.getClientProperty("html");
1145             if (view != null) {
1146                 Rectangle r = getTextRectangle();
1147                 if (r == null) {
1148                     return -1;
1149                 }
1150                 Rectangle2D.Float shape =
1151                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
1152                 Position.Bias bias[] = new Position.Bias[1];
1153                 return view.viewToModel(p.x, p.y, shape, bias);
1154             } else {
1155                 return -1;
1156             }
1157         }
1158 
1159         /**
1160          * Returns the bounding box of the character at the given
1161          * index in the string.  The bounds are returned in local
1162          * coordinates. If the index is invalid, <code>null</code> is returned.
1163          *
1164          * @param i the index into the String
1165          * @return the screen coordinates of the character's bounding box.
1166          * If the index is invalid, <code>null</code> is returned.
1167          * @since 1.3
1168          */
1169         public Rectangle getCharacterBounds(int i) {
1170             View view = (View) JLabel.this.getClientProperty("html");
1171             if (view != null) {
1172                 Rectangle r = getTextRectangle();
1173         if (r == null) {
1174             return null;
1175         }
1176                 Rectangle2D.Float shape =
1177                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
1178                 try {
1179                     Shape charShape =
1180                         view.modelToView(i, shape, Position.Bias.Forward);
1181                     return charShape.getBounds();
1182                 } catch (BadLocationException e) {
1183                     return null;
1184                 }
1185             } else {
1186                 return null;
1187             }
1188         }
1189 
1190         /**
1191          * Return the number of characters (valid indicies)
1192          *
1193          * @return the number of characters
1194          * @since 1.3
1195          */
1196         public int getCharCount() {
1197             View view = (View) JLabel.this.getClientProperty("html");
1198             if (view != null) {
1199                 Document d = view.getDocument();
1200                 if (d instanceof StyledDocument) {
1201                     StyledDocument doc = (StyledDocument)d;
1202                     return doc.getLength();
1203                 }
1204             }
1205             return accessibleContext.getAccessibleName().length();
1206         }
1207 
1208         /**
1209          * Return the zero-based offset of the caret.
1210          *
1211          * Note: That to the right of the caret will have the same index
1212          * value as the offset (the caret is between two characters).
1213          * @return the zero-based offset of the caret.
1214          * @since 1.3
1215          */
1216         public int getCaretPosition() {
1217             // There is no caret.
1218             return -1;
1219         }
1220 
1221         /**
1222          * Returns the String at a given index.
1223          *
1224          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1225          * or AccessibleText.SENTENCE to retrieve
1226          * @param index an index within the text &gt;= 0
1227          * @return the letter, word, or sentence,
1228          *   null for an invalid index or part
1229          * @since 1.3
1230          */
1231         public String getAtIndex(int part, int index) {
1232             if (index < 0 || index >= getCharCount()) {
1233                 return null;
1234             }
1235             switch (part) {
1236             case AccessibleText.CHARACTER:
1237                 try {
1238                     return getText(index, 1);
1239                 } catch (BadLocationException e) {
1240                     return null;
1241                 }
1242             case AccessibleText.WORD:
1243                 try {
1244                     String s = getText(0, getCharCount());
1245                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
1246                     words.setText(s);
1247                     int end = words.following(index);
1248                     return s.substring(words.previous(), end);
1249                 } catch (BadLocationException e) {
1250                     return null;
1251                 }
1252             case AccessibleText.SENTENCE:
1253                 try {
1254                     String s = getText(0, getCharCount());
1255                     BreakIterator sentence =
1256                         BreakIterator.getSentenceInstance(getLocale());
1257                     sentence.setText(s);
1258                     int end = sentence.following(index);
1259                     return s.substring(sentence.previous(), end);
1260                 } catch (BadLocationException e) {
1261                     return null;
1262                 }
1263             default:
1264                 return null;
1265             }
1266         }
1267 
1268         /**
1269          * Returns the String after a given index.
1270          *
1271          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1272          * or AccessibleText.SENTENCE to retrieve
1273          * @param index an index within the text &gt;= 0
1274          * @return the letter, word, or sentence, null for an invalid
1275          *  index or part
1276          * @since 1.3
1277          */
1278         public String getAfterIndex(int part, int index) {
1279             if (index < 0 || index >= getCharCount()) {
1280                 return null;
1281             }
1282             switch (part) {
1283             case AccessibleText.CHARACTER:
1284                 if (index+1 >= getCharCount()) {
1285                    return null;
1286                 }
1287                 try {
1288                     return getText(index+1, 1);
1289                 } catch (BadLocationException e) {
1290                     return null;
1291                 }
1292             case AccessibleText.WORD:
1293                 try {
1294                     String s = getText(0, getCharCount());
1295                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
1296                     words.setText(s);
1297                     int start = words.following(index);
1298                     if (start == BreakIterator.DONE || start >= s.length()) {
1299                         return null;
1300                     }
1301                     int end = words.following(start);
1302                     if (end == BreakIterator.DONE || end >= s.length()) {
1303                         return null;
1304                     }
1305                     return s.substring(start, end);
1306                 } catch (BadLocationException e) {
1307                     return null;
1308                 }
1309             case AccessibleText.SENTENCE:
1310                 try {
1311                     String s = getText(0, getCharCount());
1312                     BreakIterator sentence =
1313                         BreakIterator.getSentenceInstance(getLocale());
1314                     sentence.setText(s);
1315                     int start = sentence.following(index);
1316                     if (start == BreakIterator.DONE || start > s.length()) {
1317                         return null;
1318                     }
1319                     int end = sentence.following(start);
1320                     if (end == BreakIterator.DONE || end > s.length()) {
1321                         return null;
1322                     }
1323                     return s.substring(start, end);
1324                 } catch (BadLocationException e) {
1325                     return null;
1326                 }
1327             default:
1328                 return null;
1329             }
1330         }
1331 
1332         /**
1333          * Returns the String before a given index.
1334          *
1335          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1336          *   or AccessibleText.SENTENCE to retrieve
1337          * @param index an index within the text &gt;= 0
1338          * @return the letter, word, or sentence, null for an invalid index
1339          *  or part
1340          * @since 1.3
1341          */
1342         public String getBeforeIndex(int part, int index) {
1343             if (index < 0 || index > getCharCount()-1) {
1344                 return null;
1345             }
1346             switch (part) {
1347             case AccessibleText.CHARACTER:
1348                 if (index == 0) {
1349                     return null;
1350                 }
1351                 try {
1352                     return getText(index-1, 1);
1353                 } catch (BadLocationException e) {
1354                     return null;
1355                 }
1356             case AccessibleText.WORD:
1357                 try {
1358                     String s = getText(0, getCharCount());
1359                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
1360                     words.setText(s);
1361                     int end = words.following(index);
1362                     end = words.previous();
1363                     int start = words.previous();
1364                     if (start == BreakIterator.DONE) {
1365                         return null;
1366                     }
1367                     return s.substring(start, end);
1368                 } catch (BadLocationException e) {
1369                     return null;
1370                 }
1371             case AccessibleText.SENTENCE:
1372                 try {
1373                     String s = getText(0, getCharCount());
1374                     BreakIterator sentence =
1375                         BreakIterator.getSentenceInstance(getLocale());
1376                     sentence.setText(s);
1377                     int end = sentence.following(index);
1378                     end = sentence.previous();
1379                     int start = sentence.previous();
1380                     if (start == BreakIterator.DONE) {
1381                         return null;
1382                     }
1383                     return s.substring(start, end);
1384                 } catch (BadLocationException e) {
1385                     return null;
1386                 }
1387             default:
1388                 return null;
1389             }
1390         }
1391 
1392         /**
1393          * Return the AttributeSet for a given character at a given index
1394          *
1395          * @param i the zero-based index into the text
1396          * @return the AttributeSet of the character
1397          * @since 1.3
1398          */
1399         public AttributeSet getCharacterAttribute(int i) {
1400             View view = (View) JLabel.this.getClientProperty("html");
1401             if (view != null) {
1402                 Document d = view.getDocument();
1403                 if (d instanceof StyledDocument) {
1404                     StyledDocument doc = (StyledDocument)d;
1405                     Element elem = doc.getCharacterElement(i);
1406                     if (elem != null) {
1407                         return elem.getAttributes();
1408                     }
1409                 }
1410             }
1411             return null;
1412         }
1413 
1414         /**
1415          * Returns the start offset within the selected text.
1416          * If there is no selection, but there is
1417          * a caret, the start and end offsets will be the same.
1418          *
1419          * @return the index into the text of the start of the selection
1420          * @since 1.3
1421          */
1422         public int getSelectionStart() {
1423             // Text cannot be selected.
1424             return -1;
1425         }
1426 
1427         /**
1428          * Returns the end offset within the selected text.
1429          * If there is no selection, but there is
1430          * a caret, the start and end offsets will be the same.
1431          *
1432          * @return the index into the text of the end of the selection
1433          * @since 1.3
1434          */
1435         public int getSelectionEnd() {
1436             // Text cannot be selected.
1437             return -1;
1438         }
1439 
1440         /**
1441          * Returns the portion of the text that is selected.
1442          *
1443          * @return the String portion of the text that is selected
1444          * @since 1.3
1445          */
1446         public String getSelectedText() {
1447             // Text cannot be selected.
1448             return null;
1449         }
1450 
1451         /*
1452          * Returns the text substring starting at the specified
1453          * offset with the specified length.
1454          */
1455         private String getText(int offset, int length)
1456             throws BadLocationException {
1457 
1458             View view = (View) JLabel.this.getClientProperty("html");
1459             if (view != null) {
1460                 Document d = view.getDocument();
1461                 if (d instanceof StyledDocument) {
1462                     StyledDocument doc = (StyledDocument)d;
1463                     return doc.getText(offset, length);
1464                 }
1465             }
1466             return null;
1467         }
1468 
1469         /*
1470          * Returns the bounding rectangle for the component text.
1471          */
1472         private Rectangle getTextRectangle() {
1473 
1474             String text = JLabel.this.getText();
1475             Icon icon = (JLabel.this.isEnabled()) ? JLabel.this.getIcon() : JLabel.this.getDisabledIcon();
1476 
1477             if ((icon == null) && (text == null)) {
1478                 return null;
1479             }
1480 
1481             Rectangle paintIconR = new Rectangle();
1482             Rectangle paintTextR = new Rectangle();
1483             Rectangle paintViewR = new Rectangle();
1484             Insets paintViewInsets = new Insets(0, 0, 0, 0);
1485 
1486             paintViewInsets = JLabel.this.getInsets(paintViewInsets);
1487             paintViewR.x = paintViewInsets.left;
1488             paintViewR.y = paintViewInsets.top;
1489             paintViewR.width = JLabel.this.getWidth() - (paintViewInsets.left + paintViewInsets.right);
1490             paintViewR.height = JLabel.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
1491 
1492             String clippedText = SwingUtilities.layoutCompoundLabel(
1493                 (JComponent)JLabel.this,
1494                 getFontMetrics(getFont()),
1495                 text,
1496                 icon,
1497                 JLabel.this.getVerticalAlignment(),
1498                 JLabel.this.getHorizontalAlignment(),
1499                 JLabel.this.getVerticalTextPosition(),
1500                 JLabel.this.getHorizontalTextPosition(),
1501                 paintViewR,
1502                 paintIconR,
1503                 paintTextR,
1504                 JLabel.this.getIconTextGap());
1505 
1506             return paintTextR;
1507         }
1508 
1509         // ----- AccessibleExtendedComponent
1510 
1511         /**
1512          * Returns the AccessibleExtendedComponent
1513          *
1514          * @return the AccessibleExtendedComponent
1515          */
1516         AccessibleExtendedComponent getAccessibleExtendedComponent() {
1517             return this;
1518         }
1519 
1520         /**
1521          * Returns the tool tip text
1522          *
1523          * @return the tool tip text, if supported, of the object;
1524          * otherwise, null
1525          * @since 1.4
1526          */
1527         public String getToolTipText() {
1528             return JLabel.this.getToolTipText();
1529         }
1530 
1531         /**
1532          * Returns the titled border text
1533          *
1534          * @return the titled border text, if supported, of the object;
1535          * otherwise, null
1536          * @since 1.4
1537          */
1538         public String getTitledBorderText() {
1539             return super.getTitledBorderText();
1540         }
1541 
1542         /**
1543          * Returns key bindings associated with this object
1544          *
1545          * @return the key bindings, if supported, of the object;
1546          * otherwise, null
1547          * @see AccessibleKeyBinding
1548          * @since 1.4
1549          */
1550         public AccessibleKeyBinding getAccessibleKeyBinding() {
1551             int mnemonic = JLabel.this.getDisplayedMnemonic();
1552             if (mnemonic == 0) {
1553                 return null;
1554             }
1555             return new LabelKeyBinding(mnemonic);
1556         }
1557 
1558         class LabelKeyBinding implements AccessibleKeyBinding {
1559             int mnemonic;
1560 
1561             LabelKeyBinding(int mnemonic) {
1562                 this.mnemonic = mnemonic;
1563             }
1564 
1565             /**
1566              * Returns the number of key bindings for this object
1567              *
1568              * @return the zero-based number of key bindings for this object
1569              */
1570             public int getAccessibleKeyBindingCount() {
1571                 return 1;
1572             }
1573 
1574             /**
1575              * Returns a key binding for this object.  The value returned is an
1576              * java.lang.Object which must be cast to appropriate type depending
1577              * on the underlying implementation of the key.  For example, if the
1578              * Object returned is a javax.swing.KeyStroke, the user of this
1579              * method should do the following:
1580              * <nf><code>
1581              * Component c = <get the component that has the key bindings>
1582              * AccessibleContext ac = c.getAccessibleContext();
1583              * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
1584              * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
1585              *     Object o = akb.getAccessibleKeyBinding(i);
1586              *     if (o instanceof javax.swing.KeyStroke) {
1587              *         javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
1588              *         <do something with the key binding>
1589              *     }
1590              * }
1591              * </code></nf>
1592              *
1593              * @param i zero-based index of the key bindings
1594              * @return a javax.lang.Object which specifies the key binding
1595              * @exception IllegalArgumentException if the index is
1596              * out of bounds
1597              * @see #getAccessibleKeyBindingCount
1598              */
1599             public java.lang.Object getAccessibleKeyBinding(int i) {
1600                 if (i != 0) {
1601                     throw new IllegalArgumentException();
1602                 }
1603                 return KeyStroke.getKeyStroke(mnemonic, 0);
1604             }
1605         }
1606 
1607     }  // AccessibleJComponent
1608 }