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