001    /* JLabel.java --
002       Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing;
040    
041    import java.awt.Component;
042    import java.awt.Font;
043    import java.awt.FontMetrics;
044    import java.awt.Image;
045    import java.awt.Insets;
046    import java.awt.Point;
047    import java.awt.Rectangle;
048    import java.awt.Shape;
049    import java.awt.event.KeyEvent;
050    import java.beans.PropertyChangeEvent;
051    
052    import javax.accessibility.Accessible;
053    import javax.accessibility.AccessibleContext;
054    import javax.accessibility.AccessibleExtendedComponent;
055    import javax.accessibility.AccessibleRole;
056    import javax.accessibility.AccessibleText;
057    import javax.swing.plaf.LabelUI;
058    import javax.swing.plaf.basic.BasicHTML;
059    import javax.swing.text.AttributeSet;
060    import javax.swing.text.BadLocationException;
061    import javax.swing.text.Position;
062    import javax.swing.text.SimpleAttributeSet;
063    import javax.swing.text.View;
064    
065    /**
066     * A component that displays a static text message and/or an icon.
067     */
068    public class JLabel extends JComponent implements Accessible, SwingConstants
069    {
070    
071      /**
072       * Provides the accessibility features for the <code>JLabel</code>
073       * component.
074       */
075      protected class AccessibleJLabel
076        extends JComponent.AccessibleJComponent
077        implements AccessibleText, AccessibleExtendedComponent
078      {
079        
080        /**
081         * Returns the accessible name.
082         * 
083         * @return The accessible name.
084         */
085        public String getAccessibleName()
086        {
087          if (accessibleName != null)
088            return accessibleName;
089          if (text != null)
090            return text;
091          else
092            return super.getAccessibleName();
093        }
094        
095        /**
096         * Returns the accessible role for the <code>JLabel</code> component.
097         *
098         * @return {@link AccessibleRole#LABEL}.
099         */
100        public AccessibleRole getAccessibleRole()
101        {
102          return AccessibleRole.LABEL;
103        }
104        
105        /**
106         * Returns the selected text. This is null since JLabels
107         * are not selectable.
108         *
109         * @return <code>null</code> because JLabels cannot have selected text
110         */
111        public String getSelectedText()
112        {
113          // We return null here since JLabel's text is not selectable.
114          return null;
115        }
116    
117        /**
118         * Returns the start index of the selected text.
119         *
120         * @return the start index of the selected text
121         */
122        public int getSelectionStart()
123        {
124          // JLabel don't have selected text, so we return -1 here.
125          return -1;
126        }
127    
128        /**
129         * Returns the end index of the selected text.
130         *
131         * @return the end index of the selected text
132         */
133        public int getSelectionEnd()
134        {
135          // JLabel don't have selected text, so we return -1 here.
136          return -1;
137        }
138    
139        /**
140         * Returns an {@link AttributeSet} that reflects the text attributes of
141         * the specified character. We return an empty
142         * <code>AttributeSet</code> here, because JLabels don't support text
143         * attributes (at least not yet).
144         *
145         * @param index the index of the character
146         *
147         * @return an {@link AttributeSet} that reflects the text attributes of
148         *         the specified character
149         */
150        public AttributeSet getCharacterAttribute(int index)
151        {
152          // FIXME: Return null here for simple labels, and query the HTML
153          // view for HTML labels.
154          return new SimpleAttributeSet();
155        }
156    
157        /**
158         * Returns the character, word or sentence at the specified index. The
159         * <code>part</code> parameter determines what is returned, the character,
160         * word or sentence after the index.
161         *
162         * @param part one of {@link AccessibleText#CHARACTER},
163         *             {@link AccessibleText#WORD} or
164         *             {@link AccessibleText#SENTENCE}, specifying what is returned
165         * @param index the index
166         *
167         * @return the character, word or sentence after <code>index</code>
168         */
169        public String getAtIndex(int part, int index)
170        {
171          String result = "";
172          int startIndex = -1;
173          int endIndex = -1;
174          switch(part)
175            {
176            case AccessibleText.CHARACTER:
177              result = String.valueOf(text.charAt(index));
178              break;
179            case AccessibleText.WORD:
180              startIndex = text.lastIndexOf(' ', index);
181              endIndex = text.indexOf(' ', startIndex + 1);
182              if (endIndex == -1)
183                endIndex = startIndex + 1;
184              result = text.substring(startIndex + 1, endIndex);
185              break;
186            case AccessibleText.SENTENCE:
187            default:
188              startIndex = text.lastIndexOf('.', index);
189              endIndex = text.indexOf('.', startIndex + 1);
190              if (endIndex == -1)
191                endIndex = startIndex + 1;
192              result = text.substring(startIndex + 1, endIndex);
193              break;
194            }
195          return result;
196        }
197    
198        /**
199         * Returns the character, word or sentence after the specified index. The
200         * <code>part</code> parameter determines what is returned, the character,
201         * word or sentence after the index.
202         *
203         * @param part one of {@link AccessibleText#CHARACTER},
204         *             {@link AccessibleText#WORD} or
205         *             {@link AccessibleText#SENTENCE}, specifying what is returned
206         * @param index the index
207         *
208         * @return the character, word or sentence after <code>index</code>
209         */
210        public String getAfterIndex(int part, int index)
211        {
212          String result = "";
213          int startIndex = -1;
214          int endIndex = -1;
215          switch(part)
216            {
217            case AccessibleText.CHARACTER:
218              result = String.valueOf(text.charAt(index + 1));
219              break;
220            case AccessibleText.WORD:
221              startIndex = text.indexOf(' ', index);
222              endIndex = text.indexOf(' ', startIndex + 1);
223              if (endIndex == -1)
224                endIndex = startIndex + 1;
225              result = text.substring(startIndex + 1, endIndex);
226              break;
227            case AccessibleText.SENTENCE:
228            default:
229              startIndex = text.indexOf('.', index);
230              endIndex = text.indexOf('.', startIndex + 1);
231              if (endIndex == -1)
232                endIndex = startIndex + 1;
233              result = text.substring(startIndex + 1, endIndex);
234              break;
235            }
236          return result;
237        }
238    
239        /**
240         * Returns the character, word or sentence before the specified index. The
241         * <code>part</code> parameter determines what is returned, the character,
242         * word or sentence before the index.
243         *
244         * @param part one of {@link AccessibleText#CHARACTER},
245         *             {@link AccessibleText#WORD} or
246         *             {@link AccessibleText#SENTENCE}, specifying what is returned
247         * @param index the index
248         *
249         * @return the character, word or sentence before <code>index</code>
250         */
251        public String getBeforeIndex(int part, int index)
252        {
253          String result = "";
254          int startIndex = -1;
255          int endIndex = -1;
256          switch(part)
257            {
258            case AccessibleText.CHARACTER:
259              result = String.valueOf(text.charAt(index - 1));
260              break;
261            case AccessibleText.WORD:
262              endIndex = text.lastIndexOf(' ', index);
263              if (endIndex == -1)
264                endIndex = 0;
265              startIndex = text.lastIndexOf(' ', endIndex - 1);
266              result = text.substring(startIndex + 1, endIndex);
267              break;
268            case AccessibleText.SENTENCE:
269            default:
270              endIndex = text.lastIndexOf('.', index);
271              if (endIndex == -1)
272                endIndex = 0;
273              startIndex = text.lastIndexOf('.', endIndex - 1);
274              result = text.substring(startIndex + 1, endIndex);
275              break;
276            }
277          return result;
278        }
279    
280        /**
281         * Returns the caret position. This method returns -1 because JLabel don't
282         * have a caret.
283         *
284         * @return the caret position
285         */
286        public int getCaretPosition()
287        {
288          return -1;
289        }
290    
291        /**
292         * Returns the number of characters that are displayed by the JLabel.
293         *
294         * @return the number of characters that are displayed by the JLabel
295         */
296        public int getCharCount()
297        {
298          // FIXME: Query HTML view for HTML labels.
299          return text.length();
300        }
301    
302        /**
303         * Returns the bounding box of the character at the specified index.
304         *
305         * @param index the index of the character that we return the
306         *        bounds for
307         *
308         * @return the bounding box of the character at the specified index
309         */
310        public Rectangle getCharacterBounds(int index)
311        {
312          Rectangle bounds = null;
313          View view = (View) getClientProperty(BasicHTML.propertyKey);
314          if (view != null)
315            {
316              Rectangle textR = getTextRectangle();
317              try
318                {
319                  Shape s = view.modelToView(index, textR, Position.Bias.Forward);
320                  bounds = s.getBounds();
321                }
322              catch (BadLocationException ex)
323                {
324                  // Can't return something reasonable in this case.
325                }
326            }
327          return bounds;
328        }
329    
330        /**
331         * Returns the rectangle inside the JLabel, in which the actual text is
332         * rendered. This method has been adopted from the Mauve testcase
333         * gnu.testlet.javax.swing.JLabel.AccessibleJLabel.getCharacterBounds.
334         *
335         * @return the rectangle inside the JLabel, in which the actual text is
336         *         rendered
337         */
338        private Rectangle getTextRectangle()
339        {
340          JLabel l = JLabel.this;
341          Rectangle textR = new Rectangle();
342          Rectangle iconR = new Rectangle();
343          Insets i = l.getInsets();
344          int w = l.getWidth();
345          int h = l.getHeight();
346          Rectangle viewR = new Rectangle(i.left, i.top, w - i.left - i.right,
347                                          h - i.top - i.bottom);
348          FontMetrics fm = l.getFontMetrics(l.getFont());
349          SwingUtilities.layoutCompoundLabel(l, fm, l.getText(), l.getIcon(),
350                                             l.getVerticalAlignment(),
351                                             l.getHorizontalAlignment(),
352                                             l.getVerticalTextPosition(),
353                                             l.getHorizontalTextPosition(),
354                                             viewR, iconR, textR,
355                                             l.getIconTextGap());
356          return textR;
357        }
358    
359        /**
360         * Returns the index of the character that is located at the specified
361         * point.
362         *
363         * @param point the location that we lookup the character for
364         *
365         * @return the index of the character that is located at the specified
366         *         point
367         */
368        public int getIndexAtPoint(Point point)
369        {
370          int index = -1;
371          View view = (View) getClientProperty(BasicHTML.propertyKey);
372          if (view != null)
373            {
374              Rectangle r = getTextRectangle();
375              index = view.viewToModel(point.x, point.y, r, new Position.Bias[0]);
376            }
377          return index;
378        }
379      }
380    
381      private static final long serialVersionUID = 5496508283662221534L;
382    
383      static final String LABEL_PROPERTY = "labeledBy";
384    
385      /**
386       * The Component the label will give focus to when its mnemonic is
387       * activated.
388       */
389      protected Component labelFor;
390    
391      /** The label's text. */
392      transient String text;
393    
394      /** Where the label will be positioned horizontally. */
395      private transient int horizontalAlignment = LEADING;
396    
397      /** Where the label text will be placed horizontally relative to the icon. */
398      private transient int horizontalTextPosition = TRAILING;
399    
400      /** Where the label will be positioned vertically. */
401      private transient int verticalAlignment = CENTER;
402    
403      /** Where the label text will be place vertically relative to the icon. */
404      private transient int verticalTextPosition = CENTER;
405    
406      /** The icon painted when the label is enabled. */
407      private transient Icon icon;
408    
409      /** The icon painted when the label is disabled. */
410      private transient Icon disabledIcon;
411    
412      /** The label's mnemnonic key. */
413      private transient int displayedMnemonic = KeyEvent.VK_UNDEFINED;
414    
415      /** The index of the mnemonic character in the text. */
416      private transient int displayedMnemonicIndex = -1;
417    
418      /** The gap between the icon and the text. */
419      private transient int iconTextGap = 4;
420    
421      /**
422       * Creates a new vertically centered, horizontally on the leading edge
423       * JLabel object with text and no icon.
424       */
425      public JLabel()
426      {
427        this("", null, LEADING);
428      }
429    
430      /**
431       * Creates a new vertically and horizontally centered
432       * JLabel object with no text and the given icon.
433       *
434       * @param image The icon to use with the label, <code>null</code> permitted.
435       */
436      public JLabel(Icon image)
437      {
438        this(null, image, CENTER);
439      }
440    
441      /**
442       * Creates a new vertically centered JLabel object with no text and the
443       * given icon and horizontal alignment. By default, the text is TRAILING
444       * the image.
445       *
446       * @param image The icon to use with the label, <code>null</code> premitted.
447       * @param horizontalAlignment The horizontal alignment of the label, must be
448       * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
449       * <code>LEADING</code> or <code>TRAILING</code>.
450       */
451      public JLabel(Icon image, int horizontalAlignment)
452      {
453        this(null, image, horizontalAlignment);
454      }
455    
456      /**
457       * Creates a new horizontally leading and vertically centered JLabel 
458       * object with no icon and the given text.
459       *
460       * @param text The text to use with the label, <code>null</code> permitted.
461       */
462      public JLabel(String text)
463      {
464        this(text, null, LEADING);
465      }
466    
467      /**
468       * Creates a new vertically centered JLabel object with no icon and the
469       * given text and horizontal alignment.
470       *
471       * @param text The text to use with the label, <code>null</code> permitted.
472       * @param horizontalAlignment The horizontal alignment of the label, must be
473       * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
474       * <code>LEADING</code> or <code>TRAILING</code>.
475       */
476      public JLabel(String text, int horizontalAlignment)
477      {
478        this(text, null, horizontalAlignment);
479      }
480    
481      /**
482       * Creates a new vertically centered JLabel object with the given text,
483       * icon, and horizontal alignment.
484       *
485       * @param text The text to use with the label, <code>null</code> permitted.
486       * @param icon The icon to use with the label, <code>null</code> premitted.
487       * @param horizontalAlignment The horizontal alignment of the label, must be
488       * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
489       * <code>LEADING</code> or <code>TRAILING</code>.
490       */
491      public JLabel(String text, Icon icon, int horizontalAlignment)
492      {
493        if (horizontalAlignment != SwingConstants.LEFT  
494            && horizontalAlignment != SwingConstants.RIGHT 
495            && horizontalAlignment != SwingConstants.CENTER 
496            && horizontalAlignment != SwingConstants.LEADING 
497            && horizontalAlignment != SwingConstants.TRAILING)
498          throw new IllegalArgumentException();
499        
500        this.text = text;
501        this.icon = icon;
502        this.horizontalAlignment = horizontalAlignment;
503        setAlignmentX(0.0F);
504        setInheritsPopupMenu(true);
505        updateUI();
506      }
507    
508      /**
509       * Returns the label's UI delegate.
510       *
511       * @return The label's UI delegate.
512       */
513      public LabelUI getUI()
514      {
515        return (LabelUI) ui;
516      }
517    
518      /**
519       * Sets the label's UI delegate.
520       *
521       * @param ui The label's UI delegate (<code>null</code> not permitted).
522       */
523      public void setUI(LabelUI ui)
524      {
525        super.setUI(ui);
526      }
527    
528      /**
529       * Resets the label's UI delegate to the default UI for the current look and 
530       * feel.
531       */
532      public void updateUI()
533      {
534        setUI((LabelUI) UIManager.getUI(this));
535      }
536    
537      /**
538       * Returns a name to identify which look and feel class will be
539       * the UI delegate for this label.
540       *
541       * @return <code>"LabelUI"</code>
542       */
543      public String getUIClassID()
544      {
545        return "LabelUI";
546      }
547    
548      /**
549       * Returns a string describing the attributes for the <code>JLabel</code>
550       * component, for use in debugging.  The return value is guaranteed to be 
551       * non-<code>null</code>, but the format of the string may vary between
552       * implementations.
553       *
554       * @return A string describing the attributes of the <code>JLabel</code>.
555       */
556      protected String paramString()
557      {
558        StringBuffer sb = new StringBuffer(super.paramString());
559        sb.append(",defaultIcon=");
560        if (icon != null)
561          sb.append(icon);
562        sb.append(",disabledIcon=");
563        if (disabledIcon != null)
564          sb.append(disabledIcon);
565        sb.append(",horizontalAlignment=");
566        sb.append(SwingUtilities.convertHorizontalAlignmentCodeToString(
567            horizontalAlignment));
568        sb.append(",horizontalTextPosition=");
569        sb.append(SwingUtilities.convertHorizontalAlignmentCodeToString(
570            horizontalTextPosition));
571        sb.append(",iconTextGap=").append(iconTextGap);
572        sb.append(",labelFor=");
573        if (labelFor != null)
574          sb.append(labelFor);
575        sb.append(",text=");
576        if (text != null)
577          sb.append(text);
578        sb.append(",verticalAlignment=");
579        sb.append(SwingUtilities.convertVerticalAlignmentCodeToString(
580            verticalAlignment));
581        sb.append(",verticalTextPosition=");
582        sb.append(SwingUtilities.convertVerticalAlignmentCodeToString(
583            verticalTextPosition));
584        return sb.toString();
585      }
586    
587      /**
588       * Returns the text displayed by the label.
589       *
590       * @return The label text (possibly <code>null</code>).
591       * 
592       * @see #setText(String)
593       */
594      public String getText()
595      {
596        return text;
597      }
598    
599      /**
600       * Sets the text for the label and sends a {@link PropertyChangeEvent} (with
601       * the name 'text') to all registered listeners.  This method will also 
602       * update the <code>displayedMnemonicIndex</code>, if necessary.
603       *
604       * @param newText The text (<code>null</code> permitted).
605       * 
606       * @see #getText()
607       * @see #getDisplayedMnemonicIndex()
608       */
609      public void setText(String newText)
610      {
611        if (text == null && newText == null)
612          return;
613        if (text != null && text.equals(newText))
614          return;
615    
616        String oldText = text;
617        text = newText;
618        firePropertyChange("text", oldText, newText);
619    
620        if (text != null)
621          setDisplayedMnemonicIndex(text.toUpperCase().indexOf(displayedMnemonic));
622        else
623          setDisplayedMnemonicIndex(-1);
624        revalidate();
625        repaint();
626      }
627    
628      /**
629       * Returns the active icon. The active icon is painted when the label is 
630       * enabled.
631       *
632       * @return The active icon.
633       * 
634       * @see #setIcon(Icon)
635       * @see #getDisabledIcon()
636       */
637      public Icon getIcon()
638      {
639        return icon;
640      }
641    
642      /**
643       * Sets the icon for the label (this is a bound property with the name 
644       * 'icon'). This icon will be displayed when the label is enabled.
645       *
646       * @param newIcon The icon (<code>null</code> permitted).
647       * 
648       * @see #getIcon()
649       * @see #setDisabledIcon(Icon)
650       */
651      public void setIcon(Icon newIcon)
652      {
653        if (icon != newIcon)
654          {
655            Icon oldIcon = icon;
656            icon = newIcon;
657            firePropertyChange("icon", oldIcon, newIcon);
658            repaint();
659          }
660      }
661    
662      /**
663       * Returns the disabled icon. The disabled icon is painted when the label is 
664       * disabled. If the disabled icon is <code>null</code> and the active icon
665       * is an {@link ImageIcon}, this method returns a grayed version of the icon. 
666       * The grayed version of the icon becomes the <code>disabledIcon</code>.
667       *
668       * @return The disabled icon.
669       * 
670       * @see #setDisabledIcon(Icon)
671       */
672      public Icon getDisabledIcon()
673      {
674        if (disabledIcon == null && icon instanceof ImageIcon)
675          disabledIcon = new ImageIcon(
676              GrayFilter.createDisabledImage(((ImageIcon) icon).getImage()));
677    
678        return disabledIcon;
679      }
680    
681      /**
682       * Sets the icon displayed when the label is disabled (this is a bound
683       * property with the name 'disabledIcon').
684       *
685       * @param newIcon The disabled icon (<code>null</code> permitted).
686       * 
687       * @see #getDisabledIcon()
688       */
689      public void setDisabledIcon(Icon newIcon)
690      {
691        if (disabledIcon != newIcon)
692          {
693            Icon oldIcon = disabledIcon;
694            disabledIcon = newIcon;
695            firePropertyChange("disabledIcon", oldIcon, newIcon);
696          }
697      }
698    
699      /**
700       * Sets the keycode that will be the label's mnemonic (this is a bound
701       * property with the name 'displayedMnemonic').  If the label is used as a 
702       * label for another component, the label will give focus to that component 
703       * when the mnemonic is activated.
704       *
705       * @param mnemonic The keycode to use for the mnemonic.
706       * 
707       * @see #getDisplayedMnemonic()
708       */
709      public void setDisplayedMnemonic(int mnemonic)
710      {
711        if (displayedMnemonic != mnemonic)
712          {
713            int old = displayedMnemonic;
714            displayedMnemonic = mnemonic;
715            firePropertyChange("displayedMnemonic", old, displayedMnemonic);
716            if (text != null)
717              setDisplayedMnemonicIndex(text.toUpperCase().indexOf(mnemonic));
718          }
719      }
720    
721      /**
722       * Sets the character that will be the label's mnemonic. If the
723       * label is used as a label for another component, the label will give
724       * focus to that component when the mnemonic is activated via the keyboard.
725       *
726       * @param mnemonic The character to use for the mnemonic (this will be
727       *     converted to the equivalent upper case character).
728       *     
729       * @see #getDisplayedMnemonic()
730       */
731      public void setDisplayedMnemonic(char mnemonic)
732      {
733        setDisplayedMnemonic((int) Character.toUpperCase(mnemonic));
734      }
735    
736      /**
737       * Returns the keycode that is used for the label's mnemonic.
738       *
739       * @return The keycode that is used for the label's mnemonic.
740       * 
741       * @see #setDisplayedMnemonic(int)
742       */
743      public int getDisplayedMnemonic()
744      {
745        return displayedMnemonic;
746      }
747    
748      /**
749       * Sets the index of the character in the text that will be underlined to
750       * indicate that it is the mnemonic character for the label.  You only need
751       * to call this method if you wish to override the automatically calculated
752       * character index.  For instance, for a label "Find Next" with the mnemonic
753       * character 'n', you might wish to underline the second occurrence of 'n'
754       * rather than the first (which is the default).
755       * <br><br>
756       * Note that this method does not validate the character at the specified 
757       * index to ensure that it matches the key code returned by
758       * {@link #getDisplayedMnemonic()}.
759       *
760       * @param newIndex The index of the character to underline.
761       *
762       * @throws IllegalArgumentException If index less than -1 or index is greater
763       *         than or equal to the label length.
764       *         
765       * @see #getDisplayedMnemonicIndex()
766       * @since 1.4
767       */
768      public void setDisplayedMnemonicIndex(int newIndex)
769        throws IllegalArgumentException
770      {
771        int maxValid = -1;
772        if (text != null)
773          maxValid = text.length() - 1;
774        if (newIndex < -1 || newIndex > maxValid)
775          throw new IllegalArgumentException();
776    
777        if (newIndex != displayedMnemonicIndex)
778          {
779            int oldIndex = displayedMnemonicIndex;
780            displayedMnemonicIndex = newIndex;
781            firePropertyChange("displayedMnemonicIndex", oldIndex, newIndex);
782          }
783      }
784    
785      /**
786       * Returns the index of the character in the label's text that will be
787       * underlined (to indicate that it is the mnemonic character), or -1 if no
788       * character is to be underlined.
789       *
790       * @return The index of the character that will be underlined.
791       * 
792       * @see #setDisplayedMnemonicIndex(int)
793       * @since 1.4
794       */
795      public int getDisplayedMnemonicIndex()
796      {
797        return displayedMnemonicIndex;
798      }
799    
800      /**
801       * Checks the specified key to ensure that it is valid as a horizontal 
802       * alignment, throwing an {@link IllegalArgumentException} if the key is
803       * invalid.  Valid keys are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, 
804       * {@link #LEADING} and {@link #TRAILING}.
805       *
806       * @param key The key to check.
807       * @param message The message of the exception to be thrown if the key is
808       *        invalid.
809       *
810       * @return The key if it is valid.
811       *
812       * @throws IllegalArgumentException If the key is invalid.
813       */
814      protected int checkHorizontalKey(int key, String message)
815      {
816        if (key != LEFT && key != CENTER && key != RIGHT && key != LEADING
817            && key != TRAILING)
818          throw new IllegalArgumentException(message);
819        else
820          return key;
821      }
822    
823      /**
824       * Checks the specified key to ensure that it is valid as a vertical 
825       * alignment, throwing an {@link IllegalArgumentException} if the key is
826       * invalid.  Valid keys are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
827       *
828       * @param key The key to check.
829       * @param message The message of the exception to be thrown if the key is
830       *        invalid.
831       *
832       * @return The key if it is valid.
833       *
834       * @throws IllegalArgumentException If the key is invalid.
835       */
836      protected int checkVerticalKey(int key, String message)
837      {
838        if (key != TOP && key != BOTTOM && key != CENTER)
839          throw new IllegalArgumentException(message);
840        else
841          return key;
842      }
843    
844      /**
845       * Returns the gap between the icon and the text.
846       *
847       * @return The gap between the icon and the text.
848       * 
849       * @see #setIconTextGap(int)
850       */
851      public int getIconTextGap()
852      {
853        return iconTextGap;
854      }
855    
856      /**
857       * Sets the gap between the icon and the text, in the case that both are 
858       * visible (this is a bound property with the name 'iconTextGap'). 
859       *
860       * @param newGap The gap (in pixels).
861       * 
862       * @see #getIconTextGap()
863       */
864      public void setIconTextGap(int newGap)
865      {
866        if (iconTextGap != newGap)
867          {
868            firePropertyChange("iconTextGap", iconTextGap, newGap);
869            iconTextGap = newGap;
870          }
871      }
872    
873      /**
874       * Returns the vertical alignment of the label (one of
875       * {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}).  The default value
876       * depends on the installed look and feel, but is usually {@link #CENTER}.
877       *
878       * @return The vertical alignment.
879       * 
880       * @see #setVerticalAlignment(int)
881       */
882      public int getVerticalAlignment()
883      {
884        return verticalAlignment;
885      }
886    
887      /**
888       * Sets the vertical alignment for the label (this is a bound property with
889       * the name 'verticalAlignment').  The vertical alignment determines where 
890       * the label (icon and text) will be placed vertically within the component 
891       * bounds.  Valid alignment codes are {@link #TOP}, {@link #CENTER} and 
892       * {@link #BOTTOM}.
893       *
894       * @param alignment The vertical alignment of the label.
895       * 
896       * @throws IllegalArgumentException if <code>alignment</code> is not one of 
897       *     the specified values.
898       *     
899       * @see #getVerticalAlignment()
900       */
901      public void setVerticalAlignment(int alignment)
902      {
903        if (alignment == verticalAlignment)
904          return;
905    
906        int oldAlignment = verticalAlignment;
907        verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
908        firePropertyChange("verticalAlignment", oldAlignment, verticalAlignment);
909      }
910    
911      /**
912       * Returns the horizontal alignment of the label (one of {@link #LEFT}, 
913       * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}).
914       * The default value depends on the installed look and feel, but is usually 
915       * {@link #LEFT}.
916       *
917       * @return The horizontal alignment.
918       * 
919       * @see #setHorizontalAlignment(int)
920       */
921      public int getHorizontalAlignment()
922      {
923        return horizontalAlignment;
924      }
925    
926      /**
927       * Sets the horizontal alignment for the label (this is a bound property with
928       * the name 'horizontalAlignment').  The horizontal alignment determines where 
929       * the label (icon and text) will be placed horizontally within the 
930       * component bounds.  Valid alignment codes are {@link #LEFT}, 
931       * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}.
932       *
933       * @param alignment The horizontal alignment of the label.
934       * 
935       * @throws IllegalArgumentException if <code>alignment</code> is not one of 
936       *     the specified values.
937       *     
938       * @see #getHorizontalAlignment()
939       */
940      public void setHorizontalAlignment(int alignment)
941      {
942        if (horizontalAlignment == alignment)
943          return;
944        
945        int oldAlignment = horizontalAlignment;
946        horizontalAlignment = checkHorizontalKey(alignment, "horizontalAlignment");
947        firePropertyChange("horizontalAlignment", oldAlignment,
948                           horizontalAlignment);
949      }
950    
951      /**
952       * Returns the vertical position of the label's text relative to the icon. 
953       * This will be one of {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
954       * 
955       * @return The vertical position of the label's text relative to the icon.
956       * 
957       * @see #setVerticalTextPosition(int)
958       */
959      public int getVerticalTextPosition()
960      {
961        return verticalTextPosition;
962      }
963    
964      /**
965       * Sets the vertical position of the label's text relative to the icon (this
966       * is a bound property with the name 'verticalTextPosition').  Valid 
967       * positions are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
968       *
969       * @param textPosition The vertical text position.
970       * 
971       * @throws IllegalArgumentException if <code>textPosition</code> is not one
972       *     of the specified values.
973       */
974      public void setVerticalTextPosition(int textPosition)
975      {
976        if (textPosition != verticalTextPosition)
977          {
978            int oldPos = verticalTextPosition;
979            verticalTextPosition = checkVerticalKey(textPosition,
980                                                        "verticalTextPosition");
981            firePropertyChange("verticalTextPosition", oldPos, 
982                               verticalTextPosition);
983          }
984      }
985    
986      /**
987       * Returns the horizontal position of the label's text relative to the icon. 
988       * This will be one of {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, 
989       * {@link #LEADING} and {@link #TRAILING}.
990       * 
991       * @return The horizontal position of the label's text relative to the icon.
992       * 
993       * @see #setHorizontalTextPosition(int)
994       */
995      public int getHorizontalTextPosition()
996      {
997        return horizontalTextPosition;
998      }
999    
1000      /**
1001       * Sets the horizontal position of the label's text relative to the icon (this
1002       * is a bound property with the name 'horizontalTextPosition').  Valid 
1003       * positions are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, 
1004       * {@link #LEADING} and {@link #TRAILING}.
1005       *
1006       * @param textPosition The horizontal text position.
1007       * 
1008       * @throws IllegalArgumentException if <code>textPosition</code> is not one
1009       *     of the specified values.
1010       */
1011      public void setHorizontalTextPosition(int textPosition)
1012      {
1013        if (textPosition != horizontalTextPosition)
1014          {
1015            int oldPos = horizontalTextPosition;
1016            horizontalTextPosition = checkHorizontalKey(textPosition,
1017                                                        "horizontalTextPosition");
1018            firePropertyChange("horizontalTextPosition", oldPos, 
1019                               horizontalTextPosition);
1020          }
1021      }
1022    
1023      /**
1024       * Returns false if the current icon image (current icon will depend on 
1025       * whether the label is enabled) is not equal to the passed in image.
1026       *
1027       * @param img The image to check.
1028       * @param infoflags The bitwise inclusive OR of ABORT, ALLBITS, ERROR,
1029       *        FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, and WIDTH
1030       * @param x The x position
1031       * @param y The y position
1032       * @param w The width
1033       * @param h The height
1034       *
1035       * @return Whether the current icon image is equal to the image given.
1036       */
1037      public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
1038                                 int h)
1039      {
1040        Icon currIcon = isEnabled() ? icon : disabledIcon;
1041    
1042        // XXX: Is this the correct way to check for image equality?
1043        if (currIcon != null && currIcon instanceof ImageIcon)
1044          return (((ImageIcon) currIcon).getImage() == img);
1045    
1046        return false;
1047      }
1048    
1049      /**
1050       * Returns the component that this <code>JLabel</code> is providing the label
1051       * for.  This component will typically receive the focus when the label's 
1052       * mnemonic key is activated via the keyboard.
1053       *
1054       * @return The component (possibly <code>null</code>).
1055       */
1056      public Component getLabelFor()
1057      {
1058        return labelFor;
1059      }
1060    
1061      /**
1062       * Sets the component that this <code>JLabel</code> is providing the label
1063       * for (this is a bound property with the name 'labelFor').  This component
1064       * will typically receive the focus when the label's mnemonic key is 
1065       * activated via the keyboard.
1066       *
1067       * @param c  the component (<code>null</code> permitted).
1068       * 
1069       * @see #getLabelFor()
1070       */
1071      public void setLabelFor(Component c)
1072      {
1073        if (c != labelFor)
1074          {
1075            Component oldLabelFor = labelFor;
1076            labelFor = c;
1077            firePropertyChange("labelFor", oldLabelFor, labelFor);
1078    
1079            // We put the label into the client properties for the labeled
1080            // component so that it can be read by the AccessibleJComponent.
1081            // The other option would be to reserve a default visible field
1082            // in JComponent, but since this is relatively seldomly used, it
1083            // would be unnecessary waste of memory to do so.
1084            if (oldLabelFor instanceof JComponent)
1085              {
1086                ((JComponent) oldLabelFor).putClientProperty(LABEL_PROPERTY, null);
1087              }
1088    
1089            if (labelFor instanceof JComponent)
1090              {
1091                ((JComponent) labelFor).putClientProperty(LABEL_PROPERTY, this);
1092              }
1093    
1094          }
1095      }
1096    
1097      /**
1098       * Sets the font for the label (this a bound property with the name 'font').
1099       *
1100       * @param f The font (<code>null</code> permitted).
1101       */
1102      public void setFont(Font f)
1103      {
1104        super.setFont(f);
1105        repaint();
1106      }
1107    
1108      /**
1109       * Returns the object that provides accessibility features for this
1110       * <code>JLabel</code> component.
1111       *
1112       * @return The accessible context (an instance of {@link AccessibleJLabel}).
1113       */
1114      public AccessibleContext getAccessibleContext()
1115      {
1116        if (accessibleContext == null)
1117          accessibleContext = new AccessibleJLabel();
1118        return accessibleContext;
1119      }
1120    }