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