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