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