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