1 /*
   2  * Copyright (c) 1997, 2014, 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="http://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      *
 859      * @see SwingConstants
 860      * @beaninfo
 861      *       expert: true
 862      *        bound: true
 863      *         enum: LEFT     SwingConstants.LEFT
 864      *               CENTER   SwingConstants.CENTER
 865      *               RIGHT    SwingConstants.RIGHT
 866      *               LEADING  SwingConstants.LEADING
 867      *               TRAILING SwingConstants.TRAILING
 868      *    attribute: visualUpdate true
 869      *  description: The horizontal position of the label's text,
 870      *               relative to its image.
 871      */
 872     public void setHorizontalTextPosition(int textPosition) {
 873         int old = horizontalTextPosition;
 874         this.horizontalTextPosition = checkHorizontalKey(textPosition,
 875                                                 "horizontalTextPosition");
 876         firePropertyChange("horizontalTextPosition",
 877                            old, horizontalTextPosition);
 878         revalidate();
 879         repaint();
 880     }
 881 
 882 
 883     /**
 884      * This is overridden to return false if the current Icon's Image is
 885      * not equal to the passed in Image <code>img</code>.
 886      *
 887      * @see     java.awt.image.ImageObserver
 888      * @see     java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
 889      */
 890     public boolean imageUpdate(Image img, int infoflags,
 891                                int x, int y, int w, int h) {
 892         // Don't use getDisabledIcon, will trigger creation of icon if icon
 893         // not set.
 894         if (!isShowing() ||
 895             !SwingUtilities.doesIconReferenceImage(getIcon(), img) &&
 896             !SwingUtilities.doesIconReferenceImage(disabledIcon, img)) {
 897 
 898             return false;
 899         }
 900         return super.imageUpdate(img, infoflags, x, y, w, h);
 901     }
 902 
 903 
 904     /**
 905      * See readObject() and writeObject() in JComponent for more
 906      * information about serialization in Swing.
 907      */
 908     private void writeObject(ObjectOutputStream s) throws IOException {
 909         s.defaultWriteObject();
 910         if (getUIClassID().equals(uiClassID)) {
 911             byte count = JComponent.getWriteObjCounter(this);
 912             JComponent.setWriteObjCounter(this, --count);
 913             if (count == 0 && ui != null) {
 914                 ui.installUI(this);
 915             }
 916         }
 917     }
 918 
 919 
 920     /**
 921      * Returns a string representation of this JLabel. This method
 922      * is intended to be used only for debugging purposes, and the
 923      * content and format of the returned string may vary between
 924      * implementations. The returned string may be empty but may not
 925      * be <code>null</code>.
 926      *
 927      * @return  a string representation of this JLabel.
 928      */
 929     protected String paramString() {
 930         String textString = (text != null ?
 931                              text : "");
 932         String defaultIconString = ((defaultIcon != null)
 933                                     && (defaultIcon != this)  ?
 934                                     defaultIcon.toString() : "");
 935         String disabledIconString = ((disabledIcon != null)
 936                                      && (disabledIcon != this) ?
 937                                      disabledIcon.toString() : "");
 938         String labelForString = (labelFor  != null ?
 939                                  labelFor.toString() : "");
 940         String verticalAlignmentString;
 941         if (verticalAlignment == TOP) {
 942             verticalAlignmentString = "TOP";
 943         } else if (verticalAlignment == CENTER) {
 944             verticalAlignmentString = "CENTER";
 945         } else if (verticalAlignment == BOTTOM) {
 946             verticalAlignmentString = "BOTTOM";
 947         } else verticalAlignmentString = "";
 948         String horizontalAlignmentString;
 949         if (horizontalAlignment == LEFT) {
 950             horizontalAlignmentString = "LEFT";
 951         } else if (horizontalAlignment == CENTER) {
 952             horizontalAlignmentString = "CENTER";
 953         } else if (horizontalAlignment == RIGHT) {
 954             horizontalAlignmentString = "RIGHT";
 955         } else if (horizontalAlignment == LEADING) {
 956             horizontalAlignmentString = "LEADING";
 957         } else if (horizontalAlignment == TRAILING) {
 958             horizontalAlignmentString = "TRAILING";
 959         } else horizontalAlignmentString = "";
 960         String verticalTextPositionString;
 961         if (verticalTextPosition == TOP) {
 962             verticalTextPositionString = "TOP";
 963         } else if (verticalTextPosition == CENTER) {
 964             verticalTextPositionString = "CENTER";
 965         } else if (verticalTextPosition == BOTTOM) {
 966             verticalTextPositionString = "BOTTOM";
 967         } else verticalTextPositionString = "";
 968         String horizontalTextPositionString;
 969         if (horizontalTextPosition == LEFT) {
 970             horizontalTextPositionString = "LEFT";
 971         } else if (horizontalTextPosition == CENTER) {
 972             horizontalTextPositionString = "CENTER";
 973         } else if (horizontalTextPosition == RIGHT) {
 974             horizontalTextPositionString = "RIGHT";
 975         } else if (horizontalTextPosition == LEADING) {
 976             horizontalTextPositionString = "LEADING";
 977         } else if (horizontalTextPosition == TRAILING) {
 978             horizontalTextPositionString = "TRAILING";
 979         } else horizontalTextPositionString = "";
 980 
 981         return super.paramString() +
 982         ",defaultIcon=" + defaultIconString +
 983         ",disabledIcon=" + disabledIconString +
 984         ",horizontalAlignment=" + horizontalAlignmentString +
 985         ",horizontalTextPosition=" + horizontalTextPositionString +
 986         ",iconTextGap=" + iconTextGap +
 987         ",labelFor=" + labelForString +
 988         ",text=" + textString +
 989         ",verticalAlignment=" + verticalAlignmentString +
 990         ",verticalTextPosition=" + verticalTextPositionString;
 991     }
 992 
 993     /**
 994      * --- Accessibility Support ---
 995      */
 996 
 997     /**
 998      * Get the component this is labelling.
 999      *
1000      * @return the Component this is labelling.  Can be null if this
1001      * does not label a Component.  If the displayedMnemonic
1002      * property is set and the labelFor property is also set, the label
1003      * will call the requestFocus method of the component specified by the
1004      * labelFor property when the mnemonic is activated.
1005      *
1006      * @see #getDisplayedMnemonic
1007      * @see #setDisplayedMnemonic
1008      */
1009     public Component getLabelFor() {
1010         return labelFor;
1011     }
1012 
1013     /**
1014      * Set the component this is labelling.  Can be null if this does not
1015      * label a Component.  If the displayedMnemonic property is set
1016      * and the labelFor property is also set, the label will
1017      * call the requestFocus method of the component specified by the
1018      * labelFor property when the mnemonic is activated.
1019      *
1020      * @param c  the Component this label is for, or null if the label is
1021      *           not the label for a component
1022      *
1023      * @see #getDisplayedMnemonic
1024      * @see #setDisplayedMnemonic
1025      *
1026      * @beaninfo
1027      *        bound: true
1028      *  description: The component this is labelling.
1029      */
1030     public void setLabelFor(Component c) {
1031         Component oldC = labelFor;
1032         labelFor = c;
1033         firePropertyChange("labelFor", oldC, c);
1034 
1035         if (oldC instanceof JComponent) {
1036             ((JComponent)oldC).putClientProperty(LABELED_BY_PROPERTY, null);
1037         }
1038         if (c instanceof JComponent) {
1039             ((JComponent)c).putClientProperty(LABELED_BY_PROPERTY, this);
1040         }
1041     }
1042 
1043     /**
1044      * Get the AccessibleContext of this object
1045      *
1046      * @return the AccessibleContext of this object
1047      * @beaninfo
1048      *       expert: true
1049      *  description: The AccessibleContext associated with this Label.
1050      */
1051     public AccessibleContext getAccessibleContext() {
1052         if (accessibleContext == null) {
1053             accessibleContext = new AccessibleJLabel();
1054         }
1055         return accessibleContext;
1056     }
1057 
1058     /**
1059      * The class used to obtain the accessible role for this object.
1060      * <p>
1061      * <strong>Warning:</strong>
1062      * Serialized objects of this class will not be compatible with
1063      * future Swing releases. The current serialization support is
1064      * appropriate for short term storage or RMI between applications running
1065      * the same version of Swing.  As of 1.4, support for long term storage
1066      * of all JavaBeans&trade;
1067      * has been added to the <code>java.beans</code> package.
1068      * Please see {@link java.beans.XMLEncoder}.
1069      */
1070     @SuppressWarnings("serial")
1071     protected class AccessibleJLabel extends AccessibleJComponent
1072         implements AccessibleText, AccessibleExtendedComponent {
1073 
1074         /**
1075          * Get the accessible name of this object.
1076          *
1077          * @return the localized name of the object -- can be null if this
1078          * object does not have a name
1079          * @see AccessibleContext#setAccessibleName
1080          */
1081         public String getAccessibleName() {
1082             String name = accessibleName;
1083 
1084             if (name == null) {
1085                 name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
1086             }
1087             if (name == null) {
1088                 name = JLabel.this.getText();
1089             }
1090             if (name == null) {
1091                 name = super.getAccessibleName();
1092             }
1093             return name;
1094         }
1095 
1096         /**
1097          * Get the role of this object.
1098          *
1099          * @return an instance of AccessibleRole describing the role of the
1100          * object
1101          * @see AccessibleRole
1102          */
1103         public AccessibleRole getAccessibleRole() {
1104             return AccessibleRole.LABEL;
1105         }
1106 
1107         /**
1108          * Get the AccessibleIcons associated with this object if one
1109          * or more exist.  Otherwise return null.
1110          * @since 1.3
1111          */
1112         public AccessibleIcon [] getAccessibleIcon() {
1113             Icon icon = getIcon();
1114             if (icon instanceof Accessible) {
1115                 AccessibleContext ac =
1116                 ((Accessible)icon).getAccessibleContext();
1117                 if (ac != null && ac instanceof AccessibleIcon) {
1118                     return new AccessibleIcon[] { (AccessibleIcon)ac };
1119                 }
1120             }
1121             return null;
1122         }
1123 
1124         /**
1125          * Get the AccessibleRelationSet associated with this object if one
1126          * exists.  Otherwise return null.
1127          * @see AccessibleRelation
1128          * @since 1.3
1129          */
1130         public AccessibleRelationSet getAccessibleRelationSet() {
1131             // Check where the AccessibleContext's relation
1132             // set already contains a LABEL_FOR relation.
1133             AccessibleRelationSet relationSet
1134                 = super.getAccessibleRelationSet();
1135 
1136             if (!relationSet.contains(AccessibleRelation.LABEL_FOR)) {
1137                 Component c = JLabel.this.getLabelFor();
1138                 if (c != null) {
1139                     AccessibleRelation relation
1140                         = new AccessibleRelation(AccessibleRelation.LABEL_FOR);
1141                     relation.setTarget(c);
1142                     relationSet.add(relation);
1143                 }
1144             }
1145             return relationSet;
1146         }
1147 
1148 
1149         /* AccessibleText ---------- */
1150 
1151         public AccessibleText getAccessibleText() {
1152             View view = (View)JLabel.this.getClientProperty("html");
1153             if (view != null) {
1154                 return this;
1155             } else {
1156                 return null;
1157             }
1158         }
1159 
1160         /**
1161          * Given a point in local coordinates, return the zero-based index
1162          * of the character under that Point.  If the point is invalid,
1163          * this method returns -1.
1164          *
1165          * @param p the Point in local coordinates
1166          * @return the zero-based index of the character under Point p; if
1167          * Point is invalid returns -1.
1168          * @since 1.3
1169          */
1170         public int getIndexAtPoint(Point p) {
1171             View view = (View) JLabel.this.getClientProperty("html");
1172             if (view != null) {
1173                 Rectangle r = getTextRectangle();
1174                 if (r == null) {
1175                     return -1;
1176                 }
1177                 Rectangle2D.Float shape =
1178                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
1179                 Position.Bias bias[] = new Position.Bias[1];
1180                 return view.viewToModel(p.x, p.y, shape, bias);
1181             } else {
1182                 return -1;
1183             }
1184         }
1185 
1186         /**
1187          * Returns the bounding box of the character at the given
1188          * index in the string.  The bounds are returned in local
1189          * coordinates. If the index is invalid, <code>null</code> is returned.
1190          *
1191          * @param i the index into the String
1192          * @return the screen coordinates of the character's bounding box.
1193          * If the index is invalid, <code>null</code> is returned.
1194          * @since 1.3
1195          */
1196         public Rectangle getCharacterBounds(int i) {
1197             View view = (View) JLabel.this.getClientProperty("html");
1198             if (view != null) {
1199                 Rectangle r = getTextRectangle();
1200         if (r == null) {
1201             return null;
1202         }
1203                 Rectangle2D.Float shape =
1204                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
1205                 try {
1206                     Shape charShape =
1207                         view.modelToView(i, shape, Position.Bias.Forward);
1208                     return charShape.getBounds();
1209                 } catch (BadLocationException e) {
1210                     return null;
1211                 }
1212             } else {
1213                 return null;
1214             }
1215         }
1216 
1217         /**
1218          * Return the number of characters (valid indicies)
1219          *
1220          * @return the number of characters
1221          * @since 1.3
1222          */
1223         public int getCharCount() {
1224             View view = (View) JLabel.this.getClientProperty("html");
1225             if (view != null) {
1226                 Document d = view.getDocument();
1227                 if (d instanceof StyledDocument) {
1228                     StyledDocument doc = (StyledDocument)d;
1229                     return doc.getLength();
1230                 }
1231             }
1232             return accessibleContext.getAccessibleName().length();
1233         }
1234 
1235         /**
1236          * Return the zero-based offset of the caret.
1237          *
1238          * Note: That to the right of the caret will have the same index
1239          * value as the offset (the caret is between two characters).
1240          * @return the zero-based offset of the caret.
1241          * @since 1.3
1242          */
1243         public int getCaretPosition() {
1244             // There is no caret.
1245             return -1;
1246         }
1247 
1248         /**
1249          * Returns the String at a given index.
1250          *
1251          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1252          * or AccessibleText.SENTENCE to retrieve
1253          * @param index an index within the text &gt;= 0
1254          * @return the letter, word, or sentence,
1255          *   null for an invalid index or part
1256          * @since 1.3
1257          */
1258         public String getAtIndex(int part, int index) {
1259             if (index < 0 || index >= getCharCount()) {
1260                 return null;
1261             }
1262             switch (part) {
1263             case AccessibleText.CHARACTER:
1264                 try {
1265                     return getText(index, 1);
1266                 } catch (BadLocationException e) {
1267                     return null;
1268                 }
1269             case AccessibleText.WORD:
1270                 try {
1271                     String s = getText(0, getCharCount());
1272                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
1273                     words.setText(s);
1274                     int end = words.following(index);
1275                     return s.substring(words.previous(), end);
1276                 } catch (BadLocationException e) {
1277                     return null;
1278                 }
1279             case AccessibleText.SENTENCE:
1280                 try {
1281                     String s = getText(0, getCharCount());
1282                     BreakIterator sentence =
1283                         BreakIterator.getSentenceInstance(getLocale());
1284                     sentence.setText(s);
1285                     int end = sentence.following(index);
1286                     return s.substring(sentence.previous(), end);
1287                 } catch (BadLocationException e) {
1288                     return null;
1289                 }
1290             default:
1291                 return null;
1292             }
1293         }
1294 
1295         /**
1296          * Returns the String after a given index.
1297          *
1298          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1299          * or AccessibleText.SENTENCE to retrieve
1300          * @param index an index within the text &gt;= 0
1301          * @return the letter, word, or sentence, null for an invalid
1302          *  index or part
1303          * @since 1.3
1304          */
1305         public String getAfterIndex(int part, int index) {
1306             if (index < 0 || index >= getCharCount()) {
1307                 return null;
1308             }
1309             switch (part) {
1310             case AccessibleText.CHARACTER:
1311                 if (index+1 >= getCharCount()) {
1312                    return null;
1313                 }
1314                 try {
1315                     return getText(index+1, 1);
1316                 } catch (BadLocationException e) {
1317                     return null;
1318                 }
1319             case AccessibleText.WORD:
1320                 try {
1321                     String s = getText(0, getCharCount());
1322                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
1323                     words.setText(s);
1324                     int start = words.following(index);
1325                     if (start == BreakIterator.DONE || start >= s.length()) {
1326                         return null;
1327                     }
1328                     int end = words.following(start);
1329                     if (end == BreakIterator.DONE || end >= s.length()) {
1330                         return null;
1331                     }
1332                     return s.substring(start, end);
1333                 } catch (BadLocationException e) {
1334                     return null;
1335                 }
1336             case AccessibleText.SENTENCE:
1337                 try {
1338                     String s = getText(0, getCharCount());
1339                     BreakIterator sentence =
1340                         BreakIterator.getSentenceInstance(getLocale());
1341                     sentence.setText(s);
1342                     int start = sentence.following(index);
1343                     if (start == BreakIterator.DONE || start > s.length()) {
1344                         return null;
1345                     }
1346                     int end = sentence.following(start);
1347                     if (end == BreakIterator.DONE || end > s.length()) {
1348                         return null;
1349                     }
1350                     return s.substring(start, end);
1351                 } catch (BadLocationException e) {
1352                     return null;
1353                 }
1354             default:
1355                 return null;
1356             }
1357         }
1358 
1359         /**
1360          * Returns the String before a given index.
1361          *
1362          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1363          *   or AccessibleText.SENTENCE to retrieve
1364          * @param index an index within the text &gt;= 0
1365          * @return the letter, word, or sentence, null for an invalid index
1366          *  or part
1367          * @since 1.3
1368          */
1369         public String getBeforeIndex(int part, int index) {
1370             if (index < 0 || index > getCharCount()-1) {
1371                 return null;
1372             }
1373             switch (part) {
1374             case AccessibleText.CHARACTER:
1375                 if (index == 0) {
1376                     return null;
1377                 }
1378                 try {
1379                     return getText(index-1, 1);
1380                 } catch (BadLocationException e) {
1381                     return null;
1382                 }
1383             case AccessibleText.WORD:
1384                 try {
1385                     String s = getText(0, getCharCount());
1386                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
1387                     words.setText(s);
1388                     int end = words.following(index);
1389                     end = words.previous();
1390                     int start = words.previous();
1391                     if (start == BreakIterator.DONE) {
1392                         return null;
1393                     }
1394                     return s.substring(start, end);
1395                 } catch (BadLocationException e) {
1396                     return null;
1397                 }
1398             case AccessibleText.SENTENCE:
1399                 try {
1400                     String s = getText(0, getCharCount());
1401                     BreakIterator sentence =
1402                         BreakIterator.getSentenceInstance(getLocale());
1403                     sentence.setText(s);
1404                     int end = sentence.following(index);
1405                     end = sentence.previous();
1406                     int start = sentence.previous();
1407                     if (start == BreakIterator.DONE) {
1408                         return null;
1409                     }
1410                     return s.substring(start, end);
1411                 } catch (BadLocationException e) {
1412                     return null;
1413                 }
1414             default:
1415                 return null;
1416             }
1417         }
1418 
1419         /**
1420          * Return the AttributeSet for a given character at a given index
1421          *
1422          * @param i the zero-based index into the text
1423          * @return the AttributeSet of the character
1424          * @since 1.3
1425          */
1426         public AttributeSet getCharacterAttribute(int i) {
1427             View view = (View) JLabel.this.getClientProperty("html");
1428             if (view != null) {
1429                 Document d = view.getDocument();
1430                 if (d instanceof StyledDocument) {
1431                     StyledDocument doc = (StyledDocument)d;
1432                     Element elem = doc.getCharacterElement(i);
1433                     if (elem != null) {
1434                         return elem.getAttributes();
1435                     }
1436                 }
1437             }
1438             return null;
1439         }
1440 
1441         /**
1442          * Returns the start offset within the selected text.
1443          * If there is no selection, but there is
1444          * a caret, the start and end offsets will be the same.
1445          *
1446          * @return the index into the text of the start of the selection
1447          * @since 1.3
1448          */
1449         public int getSelectionStart() {
1450             // Text cannot be selected.
1451             return -1;
1452         }
1453 
1454         /**
1455          * Returns the end offset within the selected text.
1456          * If there is no selection, but there is
1457          * a caret, the start and end offsets will be the same.
1458          *
1459          * @return the index into the text of the end of the selection
1460          * @since 1.3
1461          */
1462         public int getSelectionEnd() {
1463             // Text cannot be selected.
1464             return -1;
1465         }
1466 
1467         /**
1468          * Returns the portion of the text that is selected.
1469          *
1470          * @return the String portion of the text that is selected
1471          * @since 1.3
1472          */
1473         public String getSelectedText() {
1474             // Text cannot be selected.
1475             return null;
1476         }
1477 
1478         /*
1479          * Returns the text substring starting at the specified
1480          * offset with the specified length.
1481          */
1482         private String getText(int offset, int length)
1483             throws BadLocationException {
1484 
1485             View view = (View) JLabel.this.getClientProperty("html");
1486             if (view != null) {
1487                 Document d = view.getDocument();
1488                 if (d instanceof StyledDocument) {
1489                     StyledDocument doc = (StyledDocument)d;
1490                     return doc.getText(offset, length);
1491                 }
1492             }
1493             return null;
1494         }
1495 
1496         /*
1497          * Returns the bounding rectangle for the component text.
1498          */
1499         private Rectangle getTextRectangle() {
1500 
1501             String text = JLabel.this.getText();
1502             Icon icon = (JLabel.this.isEnabled()) ? JLabel.this.getIcon() : JLabel.this.getDisabledIcon();
1503 
1504             if ((icon == null) && (text == null)) {
1505                 return null;
1506             }
1507 
1508             Rectangle paintIconR = new Rectangle();
1509             Rectangle paintTextR = new Rectangle();
1510             Rectangle paintViewR = new Rectangle();
1511             Insets paintViewInsets = new Insets(0, 0, 0, 0);
1512 
1513             paintViewInsets = JLabel.this.getInsets(paintViewInsets);
1514             paintViewR.x = paintViewInsets.left;
1515             paintViewR.y = paintViewInsets.top;
1516             paintViewR.width = JLabel.this.getWidth() - (paintViewInsets.left + paintViewInsets.right);
1517             paintViewR.height = JLabel.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
1518 
1519             String clippedText = SwingUtilities.layoutCompoundLabel(
1520                 (JComponent)JLabel.this,
1521                 getFontMetrics(getFont()),
1522                 text,
1523                 icon,
1524                 JLabel.this.getVerticalAlignment(),
1525                 JLabel.this.getHorizontalAlignment(),
1526                 JLabel.this.getVerticalTextPosition(),
1527                 JLabel.this.getHorizontalTextPosition(),
1528                 paintViewR,
1529                 paintIconR,
1530                 paintTextR,
1531                 JLabel.this.getIconTextGap());
1532 
1533             return paintTextR;
1534         }
1535 
1536         // ----- AccessibleExtendedComponent
1537 
1538         /**
1539          * Returns the AccessibleExtendedComponent
1540          *
1541          * @return the AccessibleExtendedComponent
1542          */
1543         AccessibleExtendedComponent getAccessibleExtendedComponent() {
1544             return this;
1545         }
1546 
1547         /**
1548          * Returns the tool tip text
1549          *
1550          * @return the tool tip text, if supported, of the object;
1551          * otherwise, null
1552          * @since 1.4
1553          */
1554         public String getToolTipText() {
1555             return JLabel.this.getToolTipText();
1556         }
1557 
1558         /**
1559          * Returns the titled border text
1560          *
1561          * @return the titled border text, if supported, of the object;
1562          * otherwise, null
1563          * @since 1.4
1564          */
1565         public String getTitledBorderText() {
1566             return super.getTitledBorderText();
1567         }
1568 
1569         /**
1570          * Returns key bindings associated with this object
1571          *
1572          * @return the key bindings, if supported, of the object;
1573          * otherwise, null
1574          * @see AccessibleKeyBinding
1575          * @since 1.4
1576          */
1577         public AccessibleKeyBinding getAccessibleKeyBinding() {
1578             int mnemonic = JLabel.this.getDisplayedMnemonic();
1579             if (mnemonic == 0) {
1580                 return null;
1581             }
1582             return new LabelKeyBinding(mnemonic);
1583         }
1584 
1585         class LabelKeyBinding implements AccessibleKeyBinding {
1586             int mnemonic;
1587 
1588             LabelKeyBinding(int mnemonic) {
1589                 this.mnemonic = mnemonic;
1590             }
1591 
1592             /**
1593              * Returns the number of key bindings for this object
1594              *
1595              * @return the zero-based number of key bindings for this object
1596              */
1597             public int getAccessibleKeyBindingCount() {
1598                 return 1;
1599             }
1600 
1601             /**
1602              * Returns a key binding for this object.  The value returned is an
1603              * java.lang.Object which must be cast to appropriate type depending
1604              * on the underlying implementation of the key.  For example, if the
1605              * Object returned is a javax.swing.KeyStroke, the user of this
1606              * method should do the following:
1607              * <nf><code>
1608              * Component c = <get the component that has the key bindings>
1609              * AccessibleContext ac = c.getAccessibleContext();
1610              * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
1611              * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
1612              *     Object o = akb.getAccessibleKeyBinding(i);
1613              *     if (o instanceof javax.swing.KeyStroke) {
1614              *         javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
1615              *         <do something with the key binding>
1616              *     }
1617              * }
1618              * </code></nf>
1619              *
1620              * @param i zero-based index of the key bindings
1621              * @return a javax.lang.Object which specifies the key binding
1622              * @exception IllegalArgumentException if the index is
1623              * out of bounds
1624              * @see #getAccessibleKeyBindingCount
1625              */
1626             public java.lang.Object getAccessibleKeyBinding(int i) {
1627                 if (i != 0) {
1628                     throw new IllegalArgumentException();
1629                 }
1630                 return KeyStroke.getKeyStroke(mnemonic, 0);
1631             }
1632         }
1633 
1634     }  // AccessibleJComponent
1635 }