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