jdk/src/java.desktop/share/classes/javax/swing/AbstractButton.java
changeset 25859 3317bb8137f4
parent 25568 b906a74c6882
child 28059 e576535359cc
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package javax.swing;
       
    26 
       
    27 import java.awt.*;
       
    28 import java.awt.event.*;
       
    29 import java.awt.image.*;
       
    30 import java.text.*;
       
    31 import java.awt.geom.*;
       
    32 import java.beans.PropertyChangeEvent;
       
    33 import java.beans.PropertyChangeListener;
       
    34 import java.beans.Transient;
       
    35 import java.util.Enumeration;
       
    36 import java.util.Vector;
       
    37 import java.io.Serializable;
       
    38 import javax.swing.event.*;
       
    39 import javax.swing.border.*;
       
    40 import javax.swing.plaf.*;
       
    41 import javax.accessibility.*;
       
    42 import javax.swing.text.*;
       
    43 import javax.swing.text.html.*;
       
    44 import javax.swing.plaf.basic.*;
       
    45 import java.util.*;
       
    46 
       
    47 /**
       
    48  * Defines common behaviors for buttons and menu items.
       
    49  * <p>
       
    50  * Buttons can be configured, and to some degree controlled, by
       
    51  * <code><a href="Action.html">Action</a></code>s.  Using an
       
    52  * <code>Action</code> with a button has many benefits beyond directly
       
    53  * configuring a button.  Refer to <a href="Action.html#buttonActions">
       
    54  * Swing Components Supporting <code>Action</code></a> for more
       
    55  * details, and you can find more information in <a
       
    56  * href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How
       
    57  * to Use Actions</a>, a section in <em>The Java Tutorial</em>.
       
    58  * <p>
       
    59  * For further information see
       
    60  * <a
       
    61  href="http://docs.oracle.com/javase/tutorial/uiswing/components/button.html">How to Use Buttons, Check Boxes, and Radio Buttons</a>,
       
    62  * a section in <em>The Java Tutorial</em>.
       
    63  * <p>
       
    64  * <strong>Warning:</strong>
       
    65  * Serialized objects of this class will not be compatible with
       
    66  * future Swing releases. The current serialization support is
       
    67  * appropriate for short term storage or RMI between applications running
       
    68  * the same version of Swing.  As of 1.4, support for long term storage
       
    69  * of all JavaBeans&trade;
       
    70  * has been added to the <code>java.beans</code> package.
       
    71  * Please see {@link java.beans.XMLEncoder}.
       
    72  *
       
    73  * @author Jeff Dinkins
       
    74  * @since 1.2
       
    75  */
       
    76 @SuppressWarnings("serial") // Same-version serialization only
       
    77 public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {
       
    78 
       
    79     // *********************************
       
    80     // ******* Button properties *******
       
    81     // *********************************
       
    82 
       
    83     /** Identifies a change in the button model. */
       
    84     public static final String MODEL_CHANGED_PROPERTY = "model";
       
    85     /** Identifies a change in the button's text. */
       
    86     public static final String TEXT_CHANGED_PROPERTY = "text";
       
    87     /** Identifies a change to the button's mnemonic. */
       
    88     public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
       
    89 
       
    90     // Text positioning and alignment
       
    91     /** Identifies a change in the button's margins. */
       
    92     public static final String MARGIN_CHANGED_PROPERTY = "margin";
       
    93     /** Identifies a change in the button's vertical alignment. */
       
    94     public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = "verticalAlignment";
       
    95     /** Identifies a change in the button's horizontal alignment. */
       
    96     public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = "horizontalAlignment";
       
    97 
       
    98     /** Identifies a change in the button's vertical text position. */
       
    99     public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = "verticalTextPosition";
       
   100     /** Identifies a change in the button's horizontal text position. */
       
   101     public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = "horizontalTextPosition";
       
   102 
       
   103     // Paint options
       
   104     /**
       
   105      * Identifies a change to having the border drawn,
       
   106      * or having it not drawn.
       
   107      */
       
   108     public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
       
   109     /**
       
   110      * Identifies a change to having the border highlighted when focused,
       
   111      * or not.
       
   112      */
       
   113     public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
       
   114     /**
       
   115      * Identifies a change from rollover enabled to disabled or back
       
   116      * to enabled.
       
   117      */
       
   118     public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = "rolloverEnabled";
       
   119     /**
       
   120      * Identifies a change to having the button paint the content area.
       
   121      */
       
   122     public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = "contentAreaFilled";
       
   123 
       
   124     // Icons
       
   125     /** Identifies a change to the icon that represents the button. */
       
   126     public static final String ICON_CHANGED_PROPERTY = "icon";
       
   127 
       
   128     /**
       
   129      * Identifies a change to the icon used when the button has been
       
   130      * pressed.
       
   131      */
       
   132     public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
       
   133     /**
       
   134      * Identifies a change to the icon used when the button has
       
   135      * been selected.
       
   136      */
       
   137     public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
       
   138 
       
   139     /**
       
   140      * Identifies a change to the icon used when the cursor is over
       
   141      * the button.
       
   142      */
       
   143     public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
       
   144     /**
       
   145      * Identifies a change to the icon used when the cursor is
       
   146      * over the button and it has been selected.
       
   147      */
       
   148     public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = "rolloverSelectedIcon";
       
   149 
       
   150     /**
       
   151      * Identifies a change to the icon used when the button has
       
   152      * been disabled.
       
   153      */
       
   154     public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
       
   155     /**
       
   156      * Identifies a change to the icon used when the button has been
       
   157      * disabled and selected.
       
   158      */
       
   159     public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = "disabledSelectedIcon";
       
   160 
       
   161 
       
   162     /** The data model that determines the button's state. */
       
   163     protected ButtonModel model                = null;
       
   164 
       
   165     private String     text                    = ""; // for BeanBox
       
   166     private Insets     margin                  = null;
       
   167     private Insets     defaultMargin           = null;
       
   168 
       
   169     // Button icons
       
   170     // PENDING(jeff) - hold icons in an array
       
   171     private Icon       defaultIcon             = null;
       
   172     private Icon       pressedIcon             = null;
       
   173     private Icon       disabledIcon            = null;
       
   174 
       
   175     private Icon       selectedIcon            = null;
       
   176     private Icon       disabledSelectedIcon    = null;
       
   177 
       
   178     private Icon       rolloverIcon            = null;
       
   179     private Icon       rolloverSelectedIcon    = null;
       
   180 
       
   181     // Display properties
       
   182     private boolean    paintBorder             = true;
       
   183     private boolean    paintFocus              = true;
       
   184     private boolean    rolloverEnabled         = false;
       
   185     private boolean    contentAreaFilled         = true;
       
   186 
       
   187     // Icon/Label Alignment
       
   188     private int        verticalAlignment       = CENTER;
       
   189     private int        horizontalAlignment     = CENTER;
       
   190 
       
   191     private int        verticalTextPosition    = CENTER;
       
   192     private int        horizontalTextPosition  = TRAILING;
       
   193 
       
   194     private int        iconTextGap             = 4;
       
   195 
       
   196     private int        mnemonic;
       
   197     private int        mnemonicIndex           = -1;
       
   198 
       
   199     private long       multiClickThreshhold    = 0;
       
   200 
       
   201     private boolean    borderPaintedSet        = false;
       
   202     private boolean    rolloverEnabledSet      = false;
       
   203     private boolean    iconTextGapSet          = false;
       
   204     private boolean    contentAreaFilledSet    = false;
       
   205 
       
   206     // Whether or not we've set the LayoutManager.
       
   207     private boolean setLayout = false;
       
   208 
       
   209     // This is only used by JButton, promoted to avoid an extra
       
   210     // boolean field in JButton
       
   211     boolean defaultCapable = true;
       
   212 
       
   213     /**
       
   214      * Combined listeners: ActionListener, ChangeListener, ItemListener.
       
   215      */
       
   216     private Handler handler;
       
   217 
       
   218     /**
       
   219      * The button model's <code>changeListener</code>.
       
   220      */
       
   221     protected ChangeListener changeListener = null;
       
   222     /**
       
   223      * The button model's <code>ActionListener</code>.
       
   224      */
       
   225     protected ActionListener actionListener = null;
       
   226     /**
       
   227      * The button model's <code>ItemListener</code>.
       
   228      */
       
   229     protected ItemListener itemListener = null;
       
   230 
       
   231     /**
       
   232      * Only one <code>ChangeEvent</code> is needed per button
       
   233      * instance since the
       
   234      * event's only state is the source property.  The source of events
       
   235      * generated is always "this".
       
   236      */
       
   237     protected transient ChangeEvent changeEvent;
       
   238 
       
   239     private boolean hideActionText = false;
       
   240 
       
   241     /**
       
   242      * Sets the <code>hideActionText</code> property, which determines
       
   243      * whether the button displays text from the <code>Action</code>.
       
   244      * This is useful only if an <code>Action</code> has been
       
   245      * installed on the button.
       
   246      *
       
   247      * @param hideActionText <code>true</code> if the button's
       
   248      *                       <code>text</code> property should not reflect
       
   249      *                       that of the <code>Action</code>; the default is
       
   250      *                       <code>false</code>
       
   251      * @see <a href="Action.html#buttonActions">Swing Components Supporting
       
   252      *      <code>Action</code></a>
       
   253      * @since 1.6
       
   254      * @beaninfo
       
   255      *        bound: true
       
   256      *    expert: true
       
   257      *  description: Whether the text of the button should come from
       
   258      *               the <code>Action</code>.
       
   259      */
       
   260     public void setHideActionText(boolean hideActionText) {
       
   261         if (hideActionText != this.hideActionText) {
       
   262             this.hideActionText = hideActionText;
       
   263             if (getAction() != null) {
       
   264                 setTextFromAction(getAction(), false);
       
   265             }
       
   266             firePropertyChange("hideActionText", !hideActionText,
       
   267                                hideActionText);
       
   268         }
       
   269     }
       
   270 
       
   271     /**
       
   272      * Returns the value of the <code>hideActionText</code> property, which
       
   273      * determines whether the button displays text from the
       
   274      * <code>Action</code>.  This is useful only if an <code>Action</code>
       
   275      * has been installed on the button.
       
   276      *
       
   277      * @return <code>true</code> if the button's <code>text</code>
       
   278      *         property should not reflect that of the
       
   279      *         <code>Action</code>; the default is <code>false</code>
       
   280      * @since 1.6
       
   281      */
       
   282     public boolean getHideActionText() {
       
   283         return hideActionText;
       
   284     }
       
   285 
       
   286     /**
       
   287      * Returns the button's text.
       
   288      * @return the buttons text
       
   289      * @see #setText
       
   290      */
       
   291     public String getText() {
       
   292         return text;
       
   293     }
       
   294 
       
   295     /**
       
   296      * Sets the button's text.
       
   297      * @param text the string used to set the text
       
   298      * @see #getText
       
   299      * @beaninfo
       
   300      *        bound: true
       
   301      *    preferred: true
       
   302      *    attribute: visualUpdate true
       
   303      *  description: The button's text.
       
   304      */
       
   305     public void setText(String text) {
       
   306         String oldValue = this.text;
       
   307         this.text = text;
       
   308         firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);
       
   309         updateDisplayedMnemonicIndex(text, getMnemonic());
       
   310 
       
   311         if (accessibleContext != null) {
       
   312             accessibleContext.firePropertyChange(
       
   313                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   314                 oldValue, text);
       
   315         }
       
   316         if (text == null || oldValue == null || !text.equals(oldValue)) {
       
   317             revalidate();
       
   318             repaint();
       
   319         }
       
   320     }
       
   321 
       
   322 
       
   323     /**
       
   324      * Returns the state of the button. True if the
       
   325      * toggle button is selected, false if it's not.
       
   326      * @return true if the toggle button is selected, otherwise false
       
   327      */
       
   328     public boolean isSelected() {
       
   329         return model.isSelected();
       
   330     }
       
   331 
       
   332     /**
       
   333      * Sets the state of the button. Note that this method does not
       
   334      * trigger an <code>actionEvent</code>.
       
   335      * Call <code>doClick</code> to perform a programmatic action change.
       
   336      *
       
   337      * @param b  true if the button is selected, otherwise false
       
   338      */
       
   339     public void setSelected(boolean b) {
       
   340         boolean oldValue = isSelected();
       
   341 
       
   342         // TIGER - 4840653
       
   343         // Removed code which fired an AccessibleState.SELECTED
       
   344         // PropertyChangeEvent since this resulted in two
       
   345         // identical events being fired since
       
   346         // AbstractButton.fireItemStateChanged also fires the
       
   347         // same event. This caused screen readers to speak the
       
   348         // name of the item twice.
       
   349 
       
   350         model.setSelected(b);
       
   351     }
       
   352 
       
   353     /**
       
   354      * Programmatically perform a "click". This does the same
       
   355      * thing as if the user had pressed and released the button.
       
   356      */
       
   357     public void doClick() {
       
   358         doClick(68);
       
   359     }
       
   360 
       
   361     /**
       
   362      * Programmatically perform a "click". This does the same
       
   363      * thing as if the user had pressed and released the button.
       
   364      * The button stays visually "pressed" for <code>pressTime</code>
       
   365      *  milliseconds.
       
   366      *
       
   367      * @param pressTime the time to "hold down" the button, in milliseconds
       
   368      */
       
   369     public void doClick(int pressTime) {
       
   370         Dimension size = getSize();
       
   371         model.setArmed(true);
       
   372         model.setPressed(true);
       
   373         paintImmediately(new Rectangle(0,0, size.width, size.height));
       
   374         try {
       
   375             Thread.sleep(pressTime);
       
   376         } catch(InterruptedException ie) {
       
   377         }
       
   378         model.setPressed(false);
       
   379         model.setArmed(false);
       
   380     }
       
   381 
       
   382     /**
       
   383      * Sets space for margin between the button's border and
       
   384      * the label. Setting to <code>null</code> will cause the button to
       
   385      * use the default margin.  The button's default <code>Border</code>
       
   386      * object will use this value to create the proper margin.
       
   387      * However, if a non-default border is set on the button,
       
   388      * it is that <code>Border</code> object's responsibility to create the
       
   389      * appropriate margin space (else this property will
       
   390      * effectively be ignored).
       
   391      *
       
   392      * @param m the space between the border and the label
       
   393      *
       
   394      * @beaninfo
       
   395      *        bound: true
       
   396      *    attribute: visualUpdate true
       
   397      *  description: The space between the button's border and the label.
       
   398      */
       
   399     public void setMargin(Insets m) {
       
   400         // Cache the old margin if it comes from the UI
       
   401         if(m instanceof UIResource) {
       
   402             defaultMargin = m;
       
   403         } else if(margin instanceof UIResource) {
       
   404             defaultMargin = margin;
       
   405         }
       
   406 
       
   407         // If the client passes in a null insets, restore the margin
       
   408         // from the UI if possible
       
   409         if(m == null && defaultMargin != null) {
       
   410             m = defaultMargin;
       
   411         }
       
   412 
       
   413         Insets old = margin;
       
   414         margin = m;
       
   415         firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
       
   416         if (old == null || !old.equals(m)) {
       
   417             revalidate();
       
   418             repaint();
       
   419         }
       
   420     }
       
   421 
       
   422     /**
       
   423      * Returns the margin between the button's border and
       
   424      * the label.
       
   425      *
       
   426      * @return an <code>Insets</code> object specifying the margin
       
   427      *          between the botton's border and the label
       
   428      * @see #setMargin
       
   429      */
       
   430     public Insets getMargin() {
       
   431         return (margin == null) ? null : (Insets) margin.clone();
       
   432     }
       
   433 
       
   434     /**
       
   435      * Returns the default icon.
       
   436      * @return the default <code>Icon</code>
       
   437      * @see #setIcon
       
   438      */
       
   439     public Icon getIcon() {
       
   440         return defaultIcon;
       
   441     }
       
   442 
       
   443     /**
       
   444      * Sets the button's default icon. This icon is
       
   445      * also used as the "pressed" and "disabled" icon if
       
   446      * there is no explicitly set pressed icon.
       
   447      *
       
   448      * @param defaultIcon the icon used as the default image
       
   449      * @see #getIcon
       
   450      * @see #setPressedIcon
       
   451      * @beaninfo
       
   452      *           bound: true
       
   453      *       attribute: visualUpdate true
       
   454      *     description: The button's default icon
       
   455      */
       
   456     public void setIcon(Icon defaultIcon) {
       
   457         Icon oldValue = this.defaultIcon;
       
   458         this.defaultIcon = defaultIcon;
       
   459 
       
   460         /* If the default icon has really changed and we had
       
   461          * generated the disabled icon for this component,
       
   462          * (i.e. setDisabledIcon() was never called) then
       
   463          * clear the disabledIcon field.
       
   464          */
       
   465         if (defaultIcon != oldValue && (disabledIcon instanceof UIResource)) {
       
   466             disabledIcon = null;
       
   467         }
       
   468 
       
   469         firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
       
   470         if (accessibleContext != null) {
       
   471             accessibleContext.firePropertyChange(
       
   472                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   473                 oldValue, defaultIcon);
       
   474         }
       
   475         if (defaultIcon != oldValue) {
       
   476             if (defaultIcon == null || oldValue == null ||
       
   477                 defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
       
   478                 defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
       
   479                 revalidate();
       
   480             }
       
   481             repaint();
       
   482         }
       
   483     }
       
   484 
       
   485     /**
       
   486      * Returns the pressed icon for the button.
       
   487      * @return the <code>pressedIcon</code> property
       
   488      * @see #setPressedIcon
       
   489      */
       
   490     public Icon getPressedIcon() {
       
   491         return pressedIcon;
       
   492     }
       
   493 
       
   494     /**
       
   495      * Sets the pressed icon for the button.
       
   496      * @param pressedIcon the icon used as the "pressed" image
       
   497      * @see #getPressedIcon
       
   498      * @beaninfo
       
   499      *        bound: true
       
   500      *    attribute: visualUpdate true
       
   501      *  description: The pressed icon for the button.
       
   502      */
       
   503     public void setPressedIcon(Icon pressedIcon) {
       
   504         Icon oldValue = this.pressedIcon;
       
   505         this.pressedIcon = pressedIcon;
       
   506         firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
       
   507         if (accessibleContext != null) {
       
   508             accessibleContext.firePropertyChange(
       
   509                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   510                 oldValue, pressedIcon);
       
   511         }
       
   512         if (pressedIcon != oldValue) {
       
   513             if (getModel().isPressed()) {
       
   514                 repaint();
       
   515             }
       
   516         }
       
   517     }
       
   518 
       
   519     /**
       
   520      * Returns the selected icon for the button.
       
   521      * @return the <code>selectedIcon</code> property
       
   522      * @see #setSelectedIcon
       
   523      */
       
   524     public Icon getSelectedIcon() {
       
   525         return selectedIcon;
       
   526     }
       
   527 
       
   528     /**
       
   529      * Sets the selected icon for the button.
       
   530      * @param selectedIcon the icon used as the "selected" image
       
   531      * @see #getSelectedIcon
       
   532      * @beaninfo
       
   533      *        bound: true
       
   534      *    attribute: visualUpdate true
       
   535      *  description: The selected icon for the button.
       
   536      */
       
   537     public void setSelectedIcon(Icon selectedIcon) {
       
   538         Icon oldValue = this.selectedIcon;
       
   539         this.selectedIcon = selectedIcon;
       
   540 
       
   541         /* If the default selected icon has really changed and we had
       
   542          * generated the disabled selected icon for this component,
       
   543          * (i.e. setDisabledSelectedIcon() was never called) then
       
   544          * clear the disabledSelectedIcon field.
       
   545          */
       
   546         if (selectedIcon != oldValue &&
       
   547             disabledSelectedIcon instanceof UIResource) {
       
   548 
       
   549             disabledSelectedIcon = null;
       
   550         }
       
   551 
       
   552         firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
       
   553         if (accessibleContext != null) {
       
   554             accessibleContext.firePropertyChange(
       
   555                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   556                 oldValue, selectedIcon);
       
   557         }
       
   558         if (selectedIcon != oldValue) {
       
   559             if (isSelected()) {
       
   560                 repaint();
       
   561             }
       
   562         }
       
   563     }
       
   564 
       
   565     /**
       
   566      * Returns the rollover icon for the button.
       
   567      * @return the <code>rolloverIcon</code> property
       
   568      * @see #setRolloverIcon
       
   569      */
       
   570     public Icon getRolloverIcon() {
       
   571         return rolloverIcon;
       
   572     }
       
   573 
       
   574     /**
       
   575      * Sets the rollover icon for the button.
       
   576      * @param rolloverIcon the icon used as the "rollover" image
       
   577      * @see #getRolloverIcon
       
   578      * @beaninfo
       
   579      *        bound: true
       
   580      *    attribute: visualUpdate true
       
   581      *  description: The rollover icon for the button.
       
   582      */
       
   583     public void setRolloverIcon(Icon rolloverIcon) {
       
   584         Icon oldValue = this.rolloverIcon;
       
   585         this.rolloverIcon = rolloverIcon;
       
   586         firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
       
   587         if (accessibleContext != null) {
       
   588             accessibleContext.firePropertyChange(
       
   589                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   590                 oldValue, rolloverIcon);
       
   591         }
       
   592         setRolloverEnabled(true);
       
   593         if (rolloverIcon != oldValue) {
       
   594             // No way to determine whether we are currently in
       
   595             // a rollover state, so repaint regardless
       
   596             repaint();
       
   597         }
       
   598 
       
   599     }
       
   600 
       
   601     /**
       
   602      * Returns the rollover selection icon for the button.
       
   603      * @return the <code>rolloverSelectedIcon</code> property
       
   604      * @see #setRolloverSelectedIcon
       
   605      */
       
   606     public Icon getRolloverSelectedIcon() {
       
   607         return rolloverSelectedIcon;
       
   608     }
       
   609 
       
   610     /**
       
   611      * Sets the rollover selected icon for the button.
       
   612      * @param rolloverSelectedIcon the icon used as the
       
   613      *          "selected rollover" image
       
   614      * @see #getRolloverSelectedIcon
       
   615      * @beaninfo
       
   616      *        bound: true
       
   617      *    attribute: visualUpdate true
       
   618      *  description: The rollover selected icon for the button.
       
   619      */
       
   620     public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
       
   621         Icon oldValue = this.rolloverSelectedIcon;
       
   622         this.rolloverSelectedIcon = rolloverSelectedIcon;
       
   623         firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
       
   624         if (accessibleContext != null) {
       
   625             accessibleContext.firePropertyChange(
       
   626                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   627                 oldValue, rolloverSelectedIcon);
       
   628         }
       
   629         setRolloverEnabled(true);
       
   630         if (rolloverSelectedIcon != oldValue) {
       
   631             // No way to determine whether we are currently in
       
   632             // a rollover state, so repaint regardless
       
   633             if (isSelected()) {
       
   634                 repaint();
       
   635             }
       
   636         }
       
   637     }
       
   638 
       
   639     /**
       
   640      * Returns the icon used by the button when it's disabled.
       
   641      * If no disabled icon has been set this will forward the call to
       
   642      * the look and feel to construct an appropriate disabled Icon.
       
   643      * <p>
       
   644      * Some look and feels might not render the disabled Icon, in which
       
   645      * case they will ignore this.
       
   646      *
       
   647      * @return the <code>disabledIcon</code> property
       
   648      * @see #getPressedIcon
       
   649      * @see #setDisabledIcon
       
   650      * @see javax.swing.LookAndFeel#getDisabledIcon
       
   651      */
       
   652     @Transient
       
   653     public Icon getDisabledIcon() {
       
   654         if (disabledIcon == null) {
       
   655             disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
       
   656             if (disabledIcon != null) {
       
   657                 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);
       
   658             }
       
   659         }
       
   660         return disabledIcon;
       
   661     }
       
   662 
       
   663     /**
       
   664      * Sets the disabled icon for the button.
       
   665      * @param disabledIcon the icon used as the disabled image
       
   666      * @see #getDisabledIcon
       
   667      * @beaninfo
       
   668      *        bound: true
       
   669      *    attribute: visualUpdate true
       
   670      *  description: The disabled icon for the button.
       
   671      */
       
   672     public void setDisabledIcon(Icon disabledIcon) {
       
   673         Icon oldValue = this.disabledIcon;
       
   674         this.disabledIcon = disabledIcon;
       
   675         firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
       
   676         if (accessibleContext != null) {
       
   677             accessibleContext.firePropertyChange(
       
   678                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   679                 oldValue, disabledIcon);
       
   680         }
       
   681         if (disabledIcon != oldValue) {
       
   682             if (!isEnabled()) {
       
   683                 repaint();
       
   684             }
       
   685         }
       
   686     }
       
   687 
       
   688     /**
       
   689      * Returns the icon used by the button when it's disabled and selected.
       
   690      * If no disabled selection icon has been set, this will forward
       
   691      * the call to the LookAndFeel to construct an appropriate disabled
       
   692      * Icon from the selection icon if it has been set and to
       
   693      * <code>getDisabledIcon()</code> otherwise.
       
   694      * <p>
       
   695      * Some look and feels might not render the disabled selected Icon, in
       
   696      * which case they will ignore this.
       
   697      *
       
   698      * @return the <code>disabledSelectedIcon</code> property
       
   699      * @see #getDisabledIcon
       
   700      * @see #setDisabledSelectedIcon
       
   701      * @see javax.swing.LookAndFeel#getDisabledSelectedIcon
       
   702      */
       
   703     public Icon getDisabledSelectedIcon() {
       
   704         if (disabledSelectedIcon == null) {
       
   705              if (selectedIcon != null) {
       
   706                  disabledSelectedIcon = UIManager.getLookAndFeel().
       
   707                          getDisabledSelectedIcon(this, getSelectedIcon());
       
   708              } else {
       
   709                  return getDisabledIcon();
       
   710              }
       
   711         }
       
   712         return disabledSelectedIcon;
       
   713     }
       
   714 
       
   715     /**
       
   716      * Sets the disabled selection icon for the button.
       
   717      * @param disabledSelectedIcon the icon used as the disabled
       
   718      *          selection image
       
   719      * @see #getDisabledSelectedIcon
       
   720      * @beaninfo
       
   721      *        bound: true
       
   722      *    attribute: visualUpdate true
       
   723      *  description: The disabled selection icon for the button.
       
   724      */
       
   725     public void setDisabledSelectedIcon(Icon disabledSelectedIcon) {
       
   726         Icon oldValue = this.disabledSelectedIcon;
       
   727         this.disabledSelectedIcon = disabledSelectedIcon;
       
   728         firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, oldValue, disabledSelectedIcon);
       
   729         if (accessibleContext != null) {
       
   730             accessibleContext.firePropertyChange(
       
   731                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
       
   732                 oldValue, disabledSelectedIcon);
       
   733         }
       
   734         if (disabledSelectedIcon != oldValue) {
       
   735             if (disabledSelectedIcon == null || oldValue == null ||
       
   736                 disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
       
   737                 disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
       
   738                 revalidate();
       
   739             }
       
   740             if (!isEnabled() && isSelected()) {
       
   741                 repaint();
       
   742             }
       
   743         }
       
   744     }
       
   745 
       
   746     /**
       
   747      * Returns the vertical alignment of the text and icon.
       
   748      *
       
   749      * @return the <code>verticalAlignment</code> property, one of the
       
   750      *          following values:
       
   751      * <ul>
       
   752      * <li>{@code SwingConstants.CENTER} (the default)
       
   753      * <li>{@code SwingConstants.TOP}
       
   754      * <li>{@code SwingConstants.BOTTOM}
       
   755      * </ul>
       
   756      */
       
   757     public int getVerticalAlignment() {
       
   758         return verticalAlignment;
       
   759     }
       
   760 
       
   761     /**
       
   762      * Sets the vertical alignment of the icon and text.
       
   763      * @param alignment one of the following values:
       
   764      * <ul>
       
   765      * <li>{@code SwingConstants.CENTER} (the default)
       
   766      * <li>{@code SwingConstants.TOP}
       
   767      * <li>{@code SwingConstants.BOTTOM}
       
   768      * </ul>
       
   769      * @throws IllegalArgumentException if the alignment is not one of the legal
       
   770      *         values listed above
       
   771      * @beaninfo
       
   772      *        bound: true
       
   773      *         enum: TOP    SwingConstants.TOP
       
   774      *               CENTER SwingConstants.CENTER
       
   775      *               BOTTOM  SwingConstants.BOTTOM
       
   776      *    attribute: visualUpdate true
       
   777      *  description: The vertical alignment of the icon and text.
       
   778      */
       
   779     public void setVerticalAlignment(int alignment) {
       
   780         if (alignment == verticalAlignment) return;
       
   781         int oldValue = verticalAlignment;
       
   782         verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
       
   783         firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment);         repaint();
       
   784     }
       
   785 
       
   786     /**
       
   787      * Returns the horizontal alignment of the icon and text.
       
   788      * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
       
   789      * but subclasses such as {@code JCheckBox} may use a different default.
       
   790      *
       
   791      * @return the <code>horizontalAlignment</code> property,
       
   792      *             one of the following values:
       
   793      * <ul>
       
   794      *   <li>{@code SwingConstants.RIGHT}
       
   795      *   <li>{@code SwingConstants.LEFT}
       
   796      *   <li>{@code SwingConstants.CENTER}
       
   797      *   <li>{@code SwingConstants.LEADING}
       
   798      *   <li>{@code SwingConstants.TRAILING}
       
   799      * </ul>
       
   800      */
       
   801     public int getHorizontalAlignment() {
       
   802         return horizontalAlignment;
       
   803     }
       
   804 
       
   805     /**
       
   806      * Sets the horizontal alignment of the icon and text.
       
   807      * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
       
   808      * but subclasses such as {@code JCheckBox} may use a different default.
       
   809      *
       
   810      * @param alignment the alignment value, one of the following values:
       
   811      * <ul>
       
   812      *   <li>{@code SwingConstants.RIGHT}
       
   813      *   <li>{@code SwingConstants.LEFT}
       
   814      *   <li>{@code SwingConstants.CENTER}
       
   815      *   <li>{@code SwingConstants.LEADING}
       
   816      *   <li>{@code SwingConstants.TRAILING}
       
   817      * </ul>
       
   818      * @throws IllegalArgumentException if the alignment is not one of the
       
   819      *         valid values
       
   820      * @beaninfo
       
   821      *        bound: true
       
   822      *         enum: LEFT     SwingConstants.LEFT
       
   823      *               CENTER   SwingConstants.CENTER
       
   824      *               RIGHT    SwingConstants.RIGHT
       
   825      *               LEADING  SwingConstants.LEADING
       
   826      *               TRAILING SwingConstants.TRAILING
       
   827      *    attribute: visualUpdate true
       
   828      *  description: The horizontal alignment of the icon and text.
       
   829      */
       
   830     public void setHorizontalAlignment(int alignment) {
       
   831         if (alignment == horizontalAlignment) return;
       
   832         int oldValue = horizontalAlignment;
       
   833         horizontalAlignment = checkHorizontalKey(alignment,
       
   834                                                  "horizontalAlignment");
       
   835         firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
       
   836                            oldValue, horizontalAlignment);
       
   837         repaint();
       
   838     }
       
   839 
       
   840 
       
   841     /**
       
   842      * Returns the vertical position of the text relative to the icon.
       
   843      * @return the <code>verticalTextPosition</code> property,
       
   844      *          one of the following values:
       
   845      * <ul>
       
   846      * <li>{@code SwingConstants.CENTER} (the default)
       
   847      * <li>{@code SwingConstants.TOP}
       
   848      * <li>{@code SwingConstants.BOTTOM}
       
   849      * </ul>
       
   850      */
       
   851     public int getVerticalTextPosition() {
       
   852         return verticalTextPosition;
       
   853     }
       
   854 
       
   855     /**
       
   856      * Sets the vertical position of the text relative to the icon.
       
   857      * @param textPosition  one of the following values:
       
   858      * <ul>
       
   859      * <li>{@code SwingConstants.CENTER} (the default)
       
   860      * <li>{@code SwingConstants.TOP}
       
   861      * <li>{@code SwingConstants.BOTTOM}
       
   862      * </ul>
       
   863      * @beaninfo
       
   864      *        bound: true
       
   865      *         enum: TOP    SwingConstants.TOP
       
   866      *               CENTER SwingConstants.CENTER
       
   867      *               BOTTOM SwingConstants.BOTTOM
       
   868      *    attribute: visualUpdate true
       
   869      *  description: The vertical position of the text relative to the icon.
       
   870      */
       
   871     public void setVerticalTextPosition(int textPosition) {
       
   872         if (textPosition == verticalTextPosition) return;
       
   873         int oldValue = verticalTextPosition;
       
   874         verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
       
   875         firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
       
   876         revalidate();
       
   877         repaint();
       
   878     }
       
   879 
       
   880     /**
       
   881      * Returns the horizontal position of the text relative to the icon.
       
   882      * @return the <code>horizontalTextPosition</code> property,
       
   883      *          one of the following values:
       
   884      * <ul>
       
   885      * <li>{@code SwingConstants.RIGHT}
       
   886      * <li>{@code SwingConstants.LEFT}
       
   887      * <li>{@code SwingConstants.CENTER}
       
   888      * <li>{@code SwingConstants.LEADING}
       
   889      * <li>{@code SwingConstants.TRAILING} (the default)
       
   890      * </ul>
       
   891      */
       
   892     public int getHorizontalTextPosition() {
       
   893         return horizontalTextPosition;
       
   894     }
       
   895 
       
   896     /**
       
   897      * Sets the horizontal position of the text relative to the icon.
       
   898      * @param textPosition one of the following values:
       
   899      * <ul>
       
   900      * <li>{@code SwingConstants.RIGHT}
       
   901      * <li>{@code SwingConstants.LEFT}
       
   902      * <li>{@code SwingConstants.CENTER}
       
   903      * <li>{@code SwingConstants.LEADING}
       
   904      * <li>{@code SwingConstants.TRAILING} (the default)
       
   905      * </ul>
       
   906      * @exception IllegalArgumentException if <code>textPosition</code>
       
   907      *          is not one of the legal values listed above
       
   908      * @beaninfo
       
   909      *        bound: true
       
   910      *         enum: LEFT     SwingConstants.LEFT
       
   911      *               CENTER   SwingConstants.CENTER
       
   912      *               RIGHT    SwingConstants.RIGHT
       
   913      *               LEADING  SwingConstants.LEADING
       
   914      *               TRAILING SwingConstants.TRAILING
       
   915      *    attribute: visualUpdate true
       
   916      *  description: The horizontal position of the text relative to the icon.
       
   917      */
       
   918     public void setHorizontalTextPosition(int textPosition) {
       
   919         if (textPosition == horizontalTextPosition) return;
       
   920         int oldValue = horizontalTextPosition;
       
   921         horizontalTextPosition = checkHorizontalKey(textPosition,
       
   922                                                     "horizontalTextPosition");
       
   923         firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
       
   924                            oldValue,
       
   925                            horizontalTextPosition);
       
   926         revalidate();
       
   927         repaint();
       
   928     }
       
   929 
       
   930     /**
       
   931      * Returns the amount of space between the text and the icon
       
   932      * displayed in this button.
       
   933      *
       
   934      * @return an int equal to the number of pixels between the text
       
   935      *         and the icon.
       
   936      * @since 1.4
       
   937      * @see #setIconTextGap
       
   938      */
       
   939     public int getIconTextGap() {
       
   940         return iconTextGap;
       
   941     }
       
   942 
       
   943     /**
       
   944      * If both the icon and text properties are set, this property
       
   945      * defines the space between them.
       
   946      * <p>
       
   947      * The default value of this property is 4 pixels.
       
   948      * <p>
       
   949      * This is a JavaBeans bound property.
       
   950      *
       
   951      * @param iconTextGap the space between icon and text if these properties are set.
       
   952      * @since 1.4
       
   953      * @see #getIconTextGap
       
   954      * @beaninfo
       
   955      *        bound: true
       
   956      *    attribute: visualUpdate true
       
   957      *  description: If both the icon and text properties are set, this
       
   958      *               property defines the space between them.
       
   959      */
       
   960     public void setIconTextGap(int iconTextGap) {
       
   961         int oldValue = this.iconTextGap;
       
   962         this.iconTextGap = iconTextGap;
       
   963         iconTextGapSet = true;
       
   964         firePropertyChange("iconTextGap", oldValue, iconTextGap);
       
   965         if (iconTextGap != oldValue) {
       
   966             revalidate();
       
   967             repaint();
       
   968         }
       
   969     }
       
   970 
       
   971     /**
       
   972      * Verify that the {@code key} argument is a legal value for the
       
   973      * {@code horizontalAlignment} and {@code horizontalTextPosition}
       
   974      * properties. Valid values are:
       
   975      * <ul>
       
   976      *   <li>{@code SwingConstants.RIGHT}
       
   977      *   <li>{@code SwingConstants.LEFT}
       
   978      *   <li>{@code SwingConstants.CENTER}
       
   979      *   <li>{@code SwingConstants.LEADING}
       
   980      *   <li>{@code SwingConstants.TRAILING}
       
   981      * </ul>
       
   982      *
       
   983      * @param key the property value to check
       
   984      * @param exception the message to use in the
       
   985      *        {@code IllegalArgumentException} that is thrown for an invalid
       
   986      *        value
       
   987      * @return the {@code key} argument
       
   988      * @exception IllegalArgumentException if key is not one of the legal
       
   989      *            values listed above
       
   990      * @see #setHorizontalTextPosition
       
   991      * @see #setHorizontalAlignment
       
   992      */
       
   993     protected int checkHorizontalKey(int key, String exception) {
       
   994         if ((key == LEFT) ||
       
   995             (key == CENTER) ||
       
   996             (key == RIGHT) ||
       
   997             (key == LEADING) ||
       
   998             (key == TRAILING)) {
       
   999             return key;
       
  1000         } else {
       
  1001             throw new IllegalArgumentException(exception);
       
  1002         }
       
  1003     }
       
  1004 
       
  1005     /**
       
  1006      * Verify that the {@code key} argument is a legal value for the
       
  1007      * vertical properties. Valid values are:
       
  1008      * <ul>
       
  1009      *   <li>{@code SwingConstants.CENTER}
       
  1010      *   <li>{@code SwingConstants.TOP}
       
  1011      *   <li>{@code SwingConstants.BOTTOM}
       
  1012      * </ul>
       
  1013      *
       
  1014      * @param key the property value to check
       
  1015      * @param exception the message to use in the
       
  1016      *        {@code IllegalArgumentException} that is thrown for an invalid
       
  1017      *        value
       
  1018      * @return the {@code key} argument
       
  1019      * @exception IllegalArgumentException if key is not one of the legal
       
  1020      *            values listed above
       
  1021      */
       
  1022     protected int checkVerticalKey(int key, String exception) {
       
  1023         if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {
       
  1024             return key;
       
  1025         } else {
       
  1026             throw new IllegalArgumentException(exception);
       
  1027         }
       
  1028     }
       
  1029 
       
  1030     /**
       
  1031      *{@inheritDoc}
       
  1032      *
       
  1033      * @since 1.6
       
  1034      */
       
  1035     public void removeNotify() {
       
  1036         super.removeNotify();
       
  1037         if(isRolloverEnabled()) {
       
  1038             getModel().setRollover(false);
       
  1039         }
       
  1040     }
       
  1041 
       
  1042     /**
       
  1043      * Sets the action command for this button.
       
  1044      * @param actionCommand the action command for this button
       
  1045      */
       
  1046     public void setActionCommand(String actionCommand) {
       
  1047         getModel().setActionCommand(actionCommand);
       
  1048     }
       
  1049 
       
  1050     /**
       
  1051      * Returns the action command for this button.
       
  1052      * @return the action command for this button
       
  1053      */
       
  1054     public String getActionCommand() {
       
  1055         String ac = getModel().getActionCommand();
       
  1056         if(ac == null) {
       
  1057             ac = getText();
       
  1058         }
       
  1059         return ac;
       
  1060     }
       
  1061 
       
  1062     private Action action;
       
  1063     private PropertyChangeListener actionPropertyChangeListener;
       
  1064 
       
  1065     /**
       
  1066      * Sets the <code>Action</code>.
       
  1067      * The new <code>Action</code> replaces any previously set
       
  1068      * <code>Action</code> but does not affect <code>ActionListeners</code>
       
  1069      * independently added with <code>addActionListener</code>.
       
  1070      * If the <code>Action</code> is already a registered
       
  1071      * <code>ActionListener</code> for the button, it is not re-registered.
       
  1072      * <p>
       
  1073      * Setting the <code>Action</code> results in immediately changing
       
  1074      * all the properties described in <a href="Action.html#buttonActions">
       
  1075      * Swing Components Supporting <code>Action</code></a>.
       
  1076      * Subsequently, the button's properties are automatically updated
       
  1077      * as the <code>Action</code>'s properties change.
       
  1078      * <p>
       
  1079      * This method uses three other methods to set
       
  1080      * and help track the <code>Action</code>'s property values.
       
  1081      * It uses the <code>configurePropertiesFromAction</code> method
       
  1082      * to immediately change the button's properties.
       
  1083      * To track changes in the <code>Action</code>'s property values,
       
  1084      * this method registers the <code>PropertyChangeListener</code>
       
  1085      * returned by <code>createActionPropertyChangeListener</code>. The
       
  1086      * default {@code PropertyChangeListener} invokes the
       
  1087      * {@code actionPropertyChanged} method when a property in the
       
  1088      * {@code Action} changes.
       
  1089      *
       
  1090      * @param a the <code>Action</code> for the <code>AbstractButton</code>,
       
  1091      *          or <code>null</code>
       
  1092      * @since 1.3
       
  1093      * @see Action
       
  1094      * @see #getAction
       
  1095      * @see #configurePropertiesFromAction
       
  1096      * @see #createActionPropertyChangeListener
       
  1097      * @see #actionPropertyChanged
       
  1098      * @beaninfo
       
  1099      *        bound: true
       
  1100      *    attribute: visualUpdate true
       
  1101      *  description: the Action instance connected with this ActionEvent source
       
  1102      */
       
  1103     public void setAction(Action a) {
       
  1104         Action oldValue = getAction();
       
  1105         if (action==null || !action.equals(a)) {
       
  1106             action = a;
       
  1107             if (oldValue!=null) {
       
  1108                 removeActionListener(oldValue);
       
  1109                 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
       
  1110                 actionPropertyChangeListener = null;
       
  1111             }
       
  1112             configurePropertiesFromAction(action);
       
  1113             if (action!=null) {
       
  1114                 // Don't add if it is already a listener
       
  1115                 if (!isListener(ActionListener.class, action)) {
       
  1116                     addActionListener(action);
       
  1117                 }
       
  1118                 // Reverse linkage:
       
  1119                 actionPropertyChangeListener = createActionPropertyChangeListener(action);
       
  1120                 action.addPropertyChangeListener(actionPropertyChangeListener);
       
  1121             }
       
  1122             firePropertyChange("action", oldValue, action);
       
  1123         }
       
  1124     }
       
  1125 
       
  1126     private boolean isListener(Class<?> c, ActionListener a) {
       
  1127         boolean isListener = false;
       
  1128         Object[] listeners = listenerList.getListenerList();
       
  1129         for (int i = listeners.length-2; i>=0; i-=2) {
       
  1130             if (listeners[i]==c && listeners[i+1]==a) {
       
  1131                     isListener=true;
       
  1132             }
       
  1133         }
       
  1134         return isListener;
       
  1135     }
       
  1136 
       
  1137     /**
       
  1138      * Returns the currently set <code>Action</code> for this
       
  1139      * <code>ActionEvent</code> source, or <code>null</code>
       
  1140      * if no <code>Action</code> is set.
       
  1141      *
       
  1142      * @return the <code>Action</code> for this <code>ActionEvent</code>
       
  1143      *          source, or <code>null</code>
       
  1144      * @since 1.3
       
  1145      * @see Action
       
  1146      * @see #setAction
       
  1147      */
       
  1148     public Action getAction() {
       
  1149         return action;
       
  1150     }
       
  1151 
       
  1152     /**
       
  1153      * Sets the properties on this button to match those in the specified
       
  1154      * <code>Action</code>.  Refer to <a href="Action.html#buttonActions">
       
  1155      * Swing Components Supporting <code>Action</code></a> for more
       
  1156      * details as to which properties this sets.
       
  1157      *
       
  1158      * @param a the <code>Action</code> from which to get the properties,
       
  1159      *          or <code>null</code>
       
  1160      * @since 1.3
       
  1161      * @see Action
       
  1162      * @see #setAction
       
  1163      */
       
  1164     protected void configurePropertiesFromAction(Action a) {
       
  1165         setMnemonicFromAction(a);
       
  1166         setTextFromAction(a, false);
       
  1167         AbstractAction.setToolTipTextFromAction(this, a);
       
  1168         setIconFromAction(a);
       
  1169         setActionCommandFromAction(a);
       
  1170         AbstractAction.setEnabledFromAction(this, a);
       
  1171         if (AbstractAction.hasSelectedKey(a) &&
       
  1172                 shouldUpdateSelectedStateFromAction()) {
       
  1173             setSelectedFromAction(a);
       
  1174         }
       
  1175         setDisplayedMnemonicIndexFromAction(a, false);
       
  1176     }
       
  1177 
       
  1178     void clientPropertyChanged(Object key, Object oldValue,
       
  1179                                Object newValue) {
       
  1180         if (key == "hideActionText") {
       
  1181             boolean current = (newValue instanceof Boolean) ?
       
  1182                                 (Boolean)newValue : false;
       
  1183             if (getHideActionText() != current) {
       
  1184                 setHideActionText(current);
       
  1185             }
       
  1186         }
       
  1187     }
       
  1188 
       
  1189     /**
       
  1190      * Button subclasses that support mirroring the selected state from
       
  1191      * the action should override this to return true.  AbstractButton's
       
  1192      * implementation returns false.
       
  1193      */
       
  1194     boolean shouldUpdateSelectedStateFromAction() {
       
  1195         return false;
       
  1196     }
       
  1197 
       
  1198     /**
       
  1199      * Updates the button's state in response to property changes in the
       
  1200      * associated action. This method is invoked from the
       
  1201      * {@code PropertyChangeListener} returned from
       
  1202      * {@code createActionPropertyChangeListener}. Subclasses do not normally
       
  1203      * need to invoke this. Subclasses that support additional {@code Action}
       
  1204      * properties should override this and
       
  1205      * {@code configurePropertiesFromAction}.
       
  1206      * <p>
       
  1207      * Refer to the table at <a href="Action.html#buttonActions">
       
  1208      * Swing Components Supporting <code>Action</code></a> for a list of
       
  1209      * the properties this method sets.
       
  1210      *
       
  1211      * @param action the <code>Action</code> associated with this button
       
  1212      * @param propertyName the name of the property that changed
       
  1213      * @since 1.6
       
  1214      * @see Action
       
  1215      * @see #configurePropertiesFromAction
       
  1216      */
       
  1217     protected void actionPropertyChanged(Action action, String propertyName) {
       
  1218         if (propertyName == Action.NAME) {
       
  1219             setTextFromAction(action, true);
       
  1220         } else if (propertyName == "enabled") {
       
  1221             AbstractAction.setEnabledFromAction(this, action);
       
  1222         } else if (propertyName == Action.SHORT_DESCRIPTION) {
       
  1223             AbstractAction.setToolTipTextFromAction(this, action);
       
  1224         } else if (propertyName == Action.SMALL_ICON) {
       
  1225             smallIconChanged(action);
       
  1226         } else if (propertyName == Action.MNEMONIC_KEY) {
       
  1227             setMnemonicFromAction(action);
       
  1228         } else if (propertyName == Action.ACTION_COMMAND_KEY) {
       
  1229             setActionCommandFromAction(action);
       
  1230         } else if (propertyName == Action.SELECTED_KEY &&
       
  1231                    AbstractAction.hasSelectedKey(action) &&
       
  1232                    shouldUpdateSelectedStateFromAction()) {
       
  1233             setSelectedFromAction(action);
       
  1234         } else if (propertyName == Action.DISPLAYED_MNEMONIC_INDEX_KEY) {
       
  1235             setDisplayedMnemonicIndexFromAction(action, true);
       
  1236         } else if (propertyName == Action.LARGE_ICON_KEY) {
       
  1237             largeIconChanged(action);
       
  1238         }
       
  1239     }
       
  1240 
       
  1241     private void setDisplayedMnemonicIndexFromAction(
       
  1242             Action a, boolean fromPropertyChange) {
       
  1243         Integer iValue = (a == null) ? null :
       
  1244                 (Integer)a.getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY);
       
  1245         if (fromPropertyChange || iValue != null) {
       
  1246             int value;
       
  1247             if (iValue == null) {
       
  1248                 value = -1;
       
  1249             } else {
       
  1250                 value = iValue;
       
  1251                 String text = getText();
       
  1252                 if (text == null || value >= text.length()) {
       
  1253                     value = -1;
       
  1254                 }
       
  1255             }
       
  1256             setDisplayedMnemonicIndex(value);
       
  1257         }
       
  1258     }
       
  1259 
       
  1260     private void setMnemonicFromAction(Action a) {
       
  1261         Integer n = (a == null) ? null :
       
  1262                                   (Integer)a.getValue(Action.MNEMONIC_KEY);
       
  1263         setMnemonic((n == null) ? '\0' : n);
       
  1264     }
       
  1265 
       
  1266     private void setTextFromAction(Action a, boolean propertyChange) {
       
  1267         boolean hideText = getHideActionText();
       
  1268         if (!propertyChange) {
       
  1269             setText((a != null && !hideText) ?
       
  1270                         (String)a.getValue(Action.NAME) : null);
       
  1271         }
       
  1272         else if (!hideText) {
       
  1273             setText((String)a.getValue(Action.NAME));
       
  1274         }
       
  1275     }
       
  1276 
       
  1277     void setIconFromAction(Action a) {
       
  1278         Icon icon = null;
       
  1279         if (a != null) {
       
  1280             icon = (Icon)a.getValue(Action.LARGE_ICON_KEY);
       
  1281             if (icon == null) {
       
  1282                 icon = (Icon)a.getValue(Action.SMALL_ICON);
       
  1283             }
       
  1284         }
       
  1285         setIcon(icon);
       
  1286     }
       
  1287 
       
  1288     void smallIconChanged(Action a) {
       
  1289         if (a.getValue(Action.LARGE_ICON_KEY) == null) {
       
  1290             setIconFromAction(a);
       
  1291         }
       
  1292     }
       
  1293 
       
  1294     void largeIconChanged(Action a) {
       
  1295         setIconFromAction(a);
       
  1296     }
       
  1297 
       
  1298     private void setActionCommandFromAction(Action a) {
       
  1299         setActionCommand((a != null) ?
       
  1300                              (String)a.getValue(Action.ACTION_COMMAND_KEY) :
       
  1301                              null);
       
  1302     }
       
  1303 
       
  1304     /**
       
  1305      * Sets the seleted state of the button from the action.  This is defined
       
  1306      * here, but not wired up.  Subclasses like JToggleButton and
       
  1307      * JCheckBoxMenuItem make use of it.
       
  1308      *
       
  1309      * @param a the Action
       
  1310      */
       
  1311     private void setSelectedFromAction(Action a) {
       
  1312         boolean selected = false;
       
  1313         if (a != null) {
       
  1314             selected = AbstractAction.isSelected(a);
       
  1315         }
       
  1316         if (selected != isSelected()) {
       
  1317             // This won't notify ActionListeners, but that should be
       
  1318             // ok as the change is coming from the Action.
       
  1319             setSelected(selected);
       
  1320             // Make sure the change actually took effect
       
  1321             if (!selected && isSelected()) {
       
  1322                 if (getModel() instanceof DefaultButtonModel) {
       
  1323                     ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup();
       
  1324                     if (group != null) {
       
  1325                         group.clearSelection();
       
  1326                     }
       
  1327                 }
       
  1328             }
       
  1329         }
       
  1330     }
       
  1331 
       
  1332     /**
       
  1333      * Creates and returns a <code>PropertyChangeListener</code> that is
       
  1334      * responsible for listening for changes from the specified
       
  1335      * <code>Action</code> and updating the appropriate properties.
       
  1336      * <p>
       
  1337      * <b>Warning:</b> If you subclass this do not create an anonymous
       
  1338      * inner class.  If you do the lifetime of the button will be tied to
       
  1339      * that of the <code>Action</code>.
       
  1340      *
       
  1341      * @param a the button's action
       
  1342      * @return the {@code PropertyChangeListener}
       
  1343      * @since 1.3
       
  1344      * @see Action
       
  1345      * @see #setAction
       
  1346      */
       
  1347     protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
       
  1348         return createActionPropertyChangeListener0(a);
       
  1349     }
       
  1350 
       
  1351 
       
  1352     PropertyChangeListener createActionPropertyChangeListener0(Action a) {
       
  1353         return new ButtonActionPropertyChangeListener(this, a);
       
  1354     }
       
  1355 
       
  1356     @SuppressWarnings("serial")
       
  1357     private static class ButtonActionPropertyChangeListener
       
  1358                  extends ActionPropertyChangeListener<AbstractButton> {
       
  1359         ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
       
  1360             super(b, a);
       
  1361         }
       
  1362         protected void actionPropertyChanged(AbstractButton button,
       
  1363                                              Action action,
       
  1364                                              PropertyChangeEvent e) {
       
  1365             if (AbstractAction.shouldReconfigure(e)) {
       
  1366                 button.configurePropertiesFromAction(action);
       
  1367             } else {
       
  1368                 button.actionPropertyChanged(action, e.getPropertyName());
       
  1369             }
       
  1370         }
       
  1371     }
       
  1372 
       
  1373     /**
       
  1374      * Gets the <code>borderPainted</code> property.
       
  1375      *
       
  1376      * @return the value of the <code>borderPainted</code> property
       
  1377      * @see #setBorderPainted
       
  1378      */
       
  1379     public boolean isBorderPainted() {
       
  1380         return paintBorder;
       
  1381     }
       
  1382 
       
  1383     /**
       
  1384      * Sets the <code>borderPainted</code> property.
       
  1385      * If <code>true</code> and the button has a border,
       
  1386      * the border is painted. The default value for the
       
  1387      * <code>borderPainted</code> property is <code>true</code>.
       
  1388      * <p>
       
  1389      * Some look and feels might not support
       
  1390      * the <code>borderPainted</code> property,
       
  1391      * in which case they ignore this.
       
  1392      *
       
  1393      * @param b if true and border property is not <code>null</code>,
       
  1394      *          the border is painted
       
  1395      * @see #isBorderPainted
       
  1396      * @beaninfo
       
  1397      *        bound: true
       
  1398      *    attribute: visualUpdate true
       
  1399      *  description: Whether the border should be painted.
       
  1400      */
       
  1401     public void setBorderPainted(boolean b) {
       
  1402         boolean oldValue = paintBorder;
       
  1403         paintBorder = b;
       
  1404         borderPaintedSet = true;
       
  1405         firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
       
  1406         if (b != oldValue) {
       
  1407             revalidate();
       
  1408             repaint();
       
  1409         }
       
  1410     }
       
  1411 
       
  1412     /**
       
  1413      * Paint the button's border if <code>BorderPainted</code>
       
  1414      * property is true and the button has a border.
       
  1415      * @param g the <code>Graphics</code> context in which to paint
       
  1416      *
       
  1417      * @see #paint
       
  1418      * @see #setBorder
       
  1419      */
       
  1420     protected void paintBorder(Graphics g) {
       
  1421         if (isBorderPainted()) {
       
  1422             super.paintBorder(g);
       
  1423         }
       
  1424     }
       
  1425 
       
  1426     /**
       
  1427      * Gets the <code>paintFocus</code> property.
       
  1428      *
       
  1429      * @return the <code>paintFocus</code> property
       
  1430      * @see #setFocusPainted
       
  1431      */
       
  1432     public boolean isFocusPainted() {
       
  1433         return paintFocus;
       
  1434     }
       
  1435 
       
  1436     /**
       
  1437      * Sets the <code>paintFocus</code> property, which must
       
  1438      * be <code>true</code> for the focus state to be painted.
       
  1439      * The default value for the <code>paintFocus</code> property
       
  1440      * is <code>true</code>.
       
  1441      * Some look and feels might not paint focus state;
       
  1442      * they will ignore this property.
       
  1443      *
       
  1444      * @param b if <code>true</code>, the focus state should be painted
       
  1445      * @see #isFocusPainted
       
  1446      * @beaninfo
       
  1447      *        bound: true
       
  1448      *    attribute: visualUpdate true
       
  1449      *  description: Whether focus should be painted
       
  1450      */
       
  1451     public void setFocusPainted(boolean b) {
       
  1452         boolean oldValue = paintFocus;
       
  1453         paintFocus = b;
       
  1454         firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
       
  1455         if (b != oldValue && isFocusOwner()) {
       
  1456             revalidate();
       
  1457             repaint();
       
  1458         }
       
  1459     }
       
  1460 
       
  1461     /**
       
  1462      * Gets the <code>contentAreaFilled</code> property.
       
  1463      *
       
  1464      * @return the <code>contentAreaFilled</code> property
       
  1465      * @see #setContentAreaFilled
       
  1466      */
       
  1467     public boolean isContentAreaFilled() {
       
  1468         return contentAreaFilled;
       
  1469     }
       
  1470 
       
  1471     /**
       
  1472      * Sets the <code>contentAreaFilled</code> property.
       
  1473      * If <code>true</code> the button will paint the content
       
  1474      * area.  If you wish to have a transparent button, such as
       
  1475      * an icon only button, for example, then you should set
       
  1476      * this to <code>false</code>. Do not call <code>setOpaque(false)</code>.
       
  1477      * The default value for the the <code>contentAreaFilled</code>
       
  1478      * property is <code>true</code>.
       
  1479      * <p>
       
  1480      * This function may cause the component's opaque property to change.
       
  1481      * <p>
       
  1482      * The exact behavior of calling this function varies on a
       
  1483      * component-by-component and L&amp;F-by-L&amp;F basis.
       
  1484      *
       
  1485      * @param b if true, the content should be filled; if false
       
  1486      *          the content area is not filled
       
  1487      * @see #isContentAreaFilled
       
  1488      * @see #setOpaque
       
  1489      * @beaninfo
       
  1490      *        bound: true
       
  1491      *    attribute: visualUpdate true
       
  1492      *  description: Whether the button should paint the content area
       
  1493      *               or leave it transparent.
       
  1494      */
       
  1495     public void setContentAreaFilled(boolean b) {
       
  1496         boolean oldValue = contentAreaFilled;
       
  1497         contentAreaFilled = b;
       
  1498         contentAreaFilledSet = true;
       
  1499         firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
       
  1500         if (b != oldValue) {
       
  1501             repaint();
       
  1502         }
       
  1503     }
       
  1504 
       
  1505     /**
       
  1506      * Gets the <code>rolloverEnabled</code> property.
       
  1507      *
       
  1508      * @return the value of the <code>rolloverEnabled</code> property
       
  1509      * @see #setRolloverEnabled
       
  1510      */
       
  1511     public boolean isRolloverEnabled() {
       
  1512         return rolloverEnabled;
       
  1513     }
       
  1514 
       
  1515     /**
       
  1516      * Sets the <code>rolloverEnabled</code> property, which
       
  1517      * must be <code>true</code> for rollover effects to occur.
       
  1518      * The default value for the <code>rolloverEnabled</code>
       
  1519      * property is <code>false</code>.
       
  1520      * Some look and feels might not implement rollover effects;
       
  1521      * they will ignore this property.
       
  1522      *
       
  1523      * @param b if <code>true</code>, rollover effects should be painted
       
  1524      * @see #isRolloverEnabled
       
  1525      * @beaninfo
       
  1526      *        bound: true
       
  1527      *    attribute: visualUpdate true
       
  1528      *  description: Whether rollover effects should be enabled.
       
  1529      */
       
  1530     public void setRolloverEnabled(boolean b) {
       
  1531         boolean oldValue = rolloverEnabled;
       
  1532         rolloverEnabled = b;
       
  1533         rolloverEnabledSet = true;
       
  1534         firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
       
  1535         if (b != oldValue) {
       
  1536             repaint();
       
  1537         }
       
  1538     }
       
  1539 
       
  1540     /**
       
  1541      * Returns the keyboard mnemonic from the the current model.
       
  1542      * @return the keyboard mnemonic from the model
       
  1543      */
       
  1544     public int getMnemonic() {
       
  1545         return mnemonic;
       
  1546     }
       
  1547 
       
  1548     /**
       
  1549      * Sets the keyboard mnemonic on the current model.
       
  1550      * The mnemonic is the key which when combined with the look and feel's
       
  1551      * mouseless modifier (usually Alt) will activate this button
       
  1552      * if focus is contained somewhere within this button's ancestor
       
  1553      * window.
       
  1554      * <p>
       
  1555      * A mnemonic must correspond to a single key on the keyboard
       
  1556      * and should be specified using one of the <code>VK_XXX</code>
       
  1557      * keycodes defined in <code>java.awt.event.KeyEvent</code>.
       
  1558      * These codes and the wider array of codes for international
       
  1559      * keyboards may be obtained through
       
  1560      * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>.
       
  1561      * Mnemonics are case-insensitive, therefore a key event
       
  1562      * with the corresponding keycode would cause the button to be
       
  1563      * activated whether or not the Shift modifier was pressed.
       
  1564      * <p>
       
  1565      * If the character defined by the mnemonic is found within
       
  1566      * the button's label string, the first occurrence of it
       
  1567      * will be underlined to indicate the mnemonic to the user.
       
  1568      *
       
  1569      * @param mnemonic the key code which represents the mnemonic
       
  1570      * @see     java.awt.event.KeyEvent
       
  1571      * @see     #setDisplayedMnemonicIndex
       
  1572      *
       
  1573      * @beaninfo
       
  1574      *        bound: true
       
  1575      *    attribute: visualUpdate true
       
  1576      *  description: the keyboard character mnemonic
       
  1577      */
       
  1578     public void setMnemonic(int mnemonic) {
       
  1579         int oldValue = getMnemonic();
       
  1580         model.setMnemonic(mnemonic);
       
  1581         updateMnemonicProperties();
       
  1582     }
       
  1583 
       
  1584     /**
       
  1585      * This method is now obsolete, please use <code>setMnemonic(int)</code>
       
  1586      * to set the mnemonic for a button.  This method is only designed
       
  1587      * to handle character values which fall between 'a' and 'z' or
       
  1588      * 'A' and 'Z'.
       
  1589      *
       
  1590      * @param mnemonic  a char specifying the mnemonic value
       
  1591      * @see #setMnemonic(int)
       
  1592      * @beaninfo
       
  1593      *        bound: true
       
  1594      *    attribute: visualUpdate true
       
  1595      *  description: the keyboard character mnemonic
       
  1596      */
       
  1597     public void setMnemonic(char mnemonic) {
       
  1598         int vk = (int) mnemonic;
       
  1599         if(vk >= 'a' && vk <='z')
       
  1600             vk -= ('a' - 'A');
       
  1601         setMnemonic(vk);
       
  1602     }
       
  1603 
       
  1604     /**
       
  1605      * Provides a hint to the look and feel as to which character in the
       
  1606      * text should be decorated to represent the mnemonic. Not all look and
       
  1607      * feels may support this. A value of -1 indicates either there is no
       
  1608      * mnemonic, the mnemonic character is not contained in the string, or
       
  1609      * the developer does not wish the mnemonic to be displayed.
       
  1610      * <p>
       
  1611      * The value of this is updated as the properties relating to the
       
  1612      * mnemonic change (such as the mnemonic itself, the text...).
       
  1613      * You should only ever have to call this if
       
  1614      * you do not wish the default character to be underlined. For example, if
       
  1615      * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
       
  1616      * to be decorated, as 'Save <u>A</u>s', you would have to invoke
       
  1617      * <code>setDisplayedMnemonicIndex(5)</code> after invoking
       
  1618      * <code>setMnemonic(KeyEvent.VK_A)</code>.
       
  1619      *
       
  1620      * @since 1.4
       
  1621      * @param index Index into the String to underline
       
  1622      * @exception IllegalArgumentException will be thrown if <code>index</code>
       
  1623      *            is &gt;= length of the text, or &lt; -1
       
  1624      * @see #getDisplayedMnemonicIndex
       
  1625      *
       
  1626      * @beaninfo
       
  1627      *        bound: true
       
  1628      *    attribute: visualUpdate true
       
  1629      *  description: the index into the String to draw the keyboard character
       
  1630      *               mnemonic at
       
  1631      */
       
  1632     public void setDisplayedMnemonicIndex(int index)
       
  1633                                           throws IllegalArgumentException {
       
  1634         int oldValue = mnemonicIndex;
       
  1635         if (index == -1) {
       
  1636             mnemonicIndex = -1;
       
  1637         } else {
       
  1638             String text = getText();
       
  1639             int textLength = (text == null) ? 0 : text.length();
       
  1640             if (index < -1 || index >= textLength) {  // index out of range
       
  1641                 throw new IllegalArgumentException("index == " + index);
       
  1642             }
       
  1643         }
       
  1644         mnemonicIndex = index;
       
  1645         firePropertyChange("displayedMnemonicIndex", oldValue, index);
       
  1646         if (index != oldValue) {
       
  1647             revalidate();
       
  1648             repaint();
       
  1649         }
       
  1650     }
       
  1651 
       
  1652     /**
       
  1653      * Returns the character, as an index, that the look and feel should
       
  1654      * provide decoration for as representing the mnemonic character.
       
  1655      *
       
  1656      * @since 1.4
       
  1657      * @return index representing mnemonic character
       
  1658      * @see #setDisplayedMnemonicIndex
       
  1659      */
       
  1660     public int getDisplayedMnemonicIndex() {
       
  1661         return mnemonicIndex;
       
  1662     }
       
  1663 
       
  1664     /**
       
  1665      * Update the displayedMnemonicIndex property. This method
       
  1666      * is called when either text or mnemonic changes. The new
       
  1667      * value of the displayedMnemonicIndex property is the index
       
  1668      * of the first occurrence of mnemonic in text.
       
  1669      */
       
  1670     private void updateDisplayedMnemonicIndex(String text, int mnemonic) {
       
  1671         setDisplayedMnemonicIndex(
       
  1672             SwingUtilities.findDisplayedMnemonicIndex(text, mnemonic));
       
  1673     }
       
  1674 
       
  1675     /**
       
  1676      * Brings the mnemonic property in accordance with model's mnemonic.
       
  1677      * This is called when model's mnemonic changes. Also updates the
       
  1678      * displayedMnemonicIndex property.
       
  1679      */
       
  1680     private void updateMnemonicProperties() {
       
  1681         int newMnemonic = model.getMnemonic();
       
  1682         if (mnemonic != newMnemonic) {
       
  1683             int oldValue = mnemonic;
       
  1684             mnemonic = newMnemonic;
       
  1685             firePropertyChange(MNEMONIC_CHANGED_PROPERTY,
       
  1686                                oldValue, mnemonic);
       
  1687             updateDisplayedMnemonicIndex(getText(), mnemonic);
       
  1688             revalidate();
       
  1689             repaint();
       
  1690         }
       
  1691     }
       
  1692 
       
  1693     /**
       
  1694      * Sets the amount of time (in milliseconds) required between
       
  1695      * mouse press events for the button to generate the corresponding
       
  1696      * action events.  After the initial mouse press occurs (and action
       
  1697      * event generated) any subsequent mouse press events which occur
       
  1698      * on intervals less than the threshhold will be ignored and no
       
  1699      * corresponding action event generated.  By default the threshhold is 0,
       
  1700      * which means that for each mouse press, an action event will be
       
  1701      * fired, no matter how quickly the mouse clicks occur.  In buttons
       
  1702      * where this behavior is not desirable (for example, the "OK" button
       
  1703      * in a dialog), this threshhold should be set to an appropriate
       
  1704      * positive value.
       
  1705      *
       
  1706      * @see #getMultiClickThreshhold
       
  1707      * @param threshhold the amount of time required between mouse
       
  1708      *        press events to generate corresponding action events
       
  1709      * @exception   IllegalArgumentException if threshhold &lt; 0
       
  1710      * @since 1.4
       
  1711      */
       
  1712     public void setMultiClickThreshhold(long threshhold) {
       
  1713         if (threshhold < 0) {
       
  1714             throw new IllegalArgumentException("threshhold must be >= 0");
       
  1715         }
       
  1716         this.multiClickThreshhold = threshhold;
       
  1717     }
       
  1718 
       
  1719     /**
       
  1720      * Gets the amount of time (in milliseconds) required between
       
  1721      * mouse press events for the button to generate the corresponding
       
  1722      * action events.
       
  1723      *
       
  1724      * @see #setMultiClickThreshhold
       
  1725      * @return the amount of time required between mouse press events
       
  1726      *         to generate corresponding action events
       
  1727      * @since 1.4
       
  1728      */
       
  1729     public long getMultiClickThreshhold() {
       
  1730         return multiClickThreshhold;
       
  1731     }
       
  1732 
       
  1733     /**
       
  1734      * Returns the model that this button represents.
       
  1735      * @return the <code>model</code> property
       
  1736      * @see #setModel
       
  1737      */
       
  1738     public ButtonModel getModel() {
       
  1739         return model;
       
  1740     }
       
  1741 
       
  1742     /**
       
  1743      * Sets the model that this button represents.
       
  1744      * @param newModel the new <code>ButtonModel</code>
       
  1745      * @see #getModel
       
  1746      * @beaninfo
       
  1747      *        bound: true
       
  1748      *  description: Model that the Button uses.
       
  1749      */
       
  1750     public void setModel(ButtonModel newModel) {
       
  1751 
       
  1752         ButtonModel oldModel = getModel();
       
  1753 
       
  1754         if (oldModel != null) {
       
  1755             oldModel.removeChangeListener(changeListener);
       
  1756             oldModel.removeActionListener(actionListener);
       
  1757             oldModel.removeItemListener(itemListener);
       
  1758             changeListener = null;
       
  1759             actionListener = null;
       
  1760             itemListener = null;
       
  1761         }
       
  1762 
       
  1763         model = newModel;
       
  1764 
       
  1765         if (newModel != null) {
       
  1766             changeListener = createChangeListener();
       
  1767             actionListener = createActionListener();
       
  1768             itemListener = createItemListener();
       
  1769             newModel.addChangeListener(changeListener);
       
  1770             newModel.addActionListener(actionListener);
       
  1771             newModel.addItemListener(itemListener);
       
  1772 
       
  1773             updateMnemonicProperties();
       
  1774             //We invoke setEnabled() from JComponent
       
  1775             //because setModel() can be called from a constructor
       
  1776             //when the button is not fully initialized
       
  1777             super.setEnabled(newModel.isEnabled());
       
  1778 
       
  1779         } else {
       
  1780             mnemonic = '\0';
       
  1781         }
       
  1782 
       
  1783         updateDisplayedMnemonicIndex(getText(), mnemonic);
       
  1784 
       
  1785         firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
       
  1786         if (newModel != oldModel) {
       
  1787             revalidate();
       
  1788             repaint();
       
  1789         }
       
  1790     }
       
  1791 
       
  1792 
       
  1793     /**
       
  1794      * Returns the L&amp;F object that renders this component.
       
  1795      * @return the ButtonUI object
       
  1796      * @see #setUI
       
  1797      */
       
  1798     public ButtonUI getUI() {
       
  1799         return (ButtonUI) ui;
       
  1800     }
       
  1801 
       
  1802 
       
  1803     /**
       
  1804      * Sets the L&amp;F object that renders this component.
       
  1805      * @param ui the <code>ButtonUI</code> L&amp;F object
       
  1806      * @see #getUI
       
  1807      * @beaninfo
       
  1808      *        bound: true
       
  1809      *       hidden: true
       
  1810      *    attribute: visualUpdate true
       
  1811      *  description: The UI object that implements the LookAndFeel.
       
  1812      */
       
  1813     public void setUI(ButtonUI ui) {
       
  1814         super.setUI(ui);
       
  1815         // disabled icons are generated by the LF so they should be unset here
       
  1816         if (disabledIcon instanceof UIResource) {
       
  1817             setDisabledIcon(null);
       
  1818         }
       
  1819         if (disabledSelectedIcon instanceof UIResource) {
       
  1820             setDisabledSelectedIcon(null);
       
  1821         }
       
  1822     }
       
  1823 
       
  1824 
       
  1825     /**
       
  1826      * Resets the UI property to a value from the current look
       
  1827      * and feel.  Subtypes of <code>AbstractButton</code>
       
  1828      * should override this to update the UI. For
       
  1829      * example, <code>JButton</code> might do the following:
       
  1830      * <pre>
       
  1831      *      setUI((ButtonUI)UIManager.getUI(
       
  1832      *          "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
       
  1833      * </pre>
       
  1834      */
       
  1835     public void updateUI() {
       
  1836     }
       
  1837 
       
  1838     /**
       
  1839      * Adds the specified component to this container at the specified
       
  1840      * index, refer to
       
  1841      * {@link java.awt.Container#addImpl(Component, Object, int)}
       
  1842      * for a complete description of this method.
       
  1843      *
       
  1844      * @param     comp the component to be added
       
  1845      * @param     constraints an object expressing layout constraints
       
  1846      *                 for this component
       
  1847      * @param     index the position in the container's list at which to
       
  1848      *                 insert the component, where <code>-1</code>
       
  1849      *                 means append to the end
       
  1850      * @exception IllegalArgumentException if <code>index</code> is invalid
       
  1851      * @exception IllegalArgumentException if adding the container's parent
       
  1852      *                  to itself
       
  1853      * @exception IllegalArgumentException if adding a window to a container
       
  1854      * @since 1.5
       
  1855      */
       
  1856     protected void addImpl(Component comp, Object constraints, int index) {
       
  1857         if (!setLayout) {
       
  1858             setLayout(new OverlayLayout(this));
       
  1859         }
       
  1860         super.addImpl(comp, constraints, index);
       
  1861     }
       
  1862 
       
  1863     /**
       
  1864      * Sets the layout manager for this container, refer to
       
  1865      * {@link java.awt.Container#setLayout(LayoutManager)}
       
  1866      * for a complete description of this method.
       
  1867      *
       
  1868      * @param mgr the specified layout manager
       
  1869      * @since 1.5
       
  1870      */
       
  1871     public void setLayout(LayoutManager mgr) {
       
  1872         setLayout = true;
       
  1873         super.setLayout(mgr);
       
  1874     }
       
  1875 
       
  1876     /**
       
  1877      * Adds a <code>ChangeListener</code> to the button.
       
  1878      * @param l the listener to be added
       
  1879      */
       
  1880     public void addChangeListener(ChangeListener l) {
       
  1881         listenerList.add(ChangeListener.class, l);
       
  1882     }
       
  1883 
       
  1884     /**
       
  1885      * Removes a ChangeListener from the button.
       
  1886      * @param l the listener to be removed
       
  1887      */
       
  1888     public void removeChangeListener(ChangeListener l) {
       
  1889         listenerList.remove(ChangeListener.class, l);
       
  1890     }
       
  1891 
       
  1892     /**
       
  1893      * Returns an array of all the <code>ChangeListener</code>s added
       
  1894      * to this AbstractButton with addChangeListener().
       
  1895      *
       
  1896      * @return all of the <code>ChangeListener</code>s added or an empty
       
  1897      *         array if no listeners have been added
       
  1898      * @since 1.4
       
  1899      */
       
  1900     public ChangeListener[] getChangeListeners() {
       
  1901         return listenerList.getListeners(ChangeListener.class);
       
  1902     }
       
  1903 
       
  1904     /**
       
  1905      * Notifies all listeners that have registered interest for
       
  1906      * notification on this event type.  The event instance
       
  1907      * is lazily created.
       
  1908      * @see EventListenerList
       
  1909      */
       
  1910     protected void fireStateChanged() {
       
  1911         // Guaranteed to return a non-null array
       
  1912         Object[] listeners = listenerList.getListenerList();
       
  1913         // Process the listeners last to first, notifying
       
  1914         // those that are interested in this event
       
  1915         for (int i = listeners.length-2; i>=0; i-=2) {
       
  1916             if (listeners[i]==ChangeListener.class) {
       
  1917                 // Lazily create the event:
       
  1918                 if (changeEvent == null)
       
  1919                     changeEvent = new ChangeEvent(this);
       
  1920                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
       
  1921             }
       
  1922         }
       
  1923     }
       
  1924 
       
  1925     /**
       
  1926      * Adds an <code>ActionListener</code> to the button.
       
  1927      * @param l the <code>ActionListener</code> to be added
       
  1928      */
       
  1929     public void addActionListener(ActionListener l) {
       
  1930         listenerList.add(ActionListener.class, l);
       
  1931     }
       
  1932 
       
  1933     /**
       
  1934      * Removes an <code>ActionListener</code> from the button.
       
  1935      * If the listener is the currently set <code>Action</code>
       
  1936      * for the button, then the <code>Action</code>
       
  1937      * is set to <code>null</code>.
       
  1938      *
       
  1939      * @param l the listener to be removed
       
  1940      */
       
  1941     public void removeActionListener(ActionListener l) {
       
  1942         if ((l != null) && (getAction() == l)) {
       
  1943             setAction(null);
       
  1944         } else {
       
  1945             listenerList.remove(ActionListener.class, l);
       
  1946         }
       
  1947     }
       
  1948 
       
  1949     /**
       
  1950      * Returns an array of all the <code>ActionListener</code>s added
       
  1951      * to this AbstractButton with addActionListener().
       
  1952      *
       
  1953      * @return all of the <code>ActionListener</code>s added or an empty
       
  1954      *         array if no listeners have been added
       
  1955      * @since 1.4
       
  1956      */
       
  1957     public ActionListener[] getActionListeners() {
       
  1958         return listenerList.getListeners(ActionListener.class);
       
  1959     }
       
  1960 
       
  1961     /**
       
  1962      * Subclasses that want to handle <code>ChangeEvents</code> differently
       
  1963      * can override this to return another <code>ChangeListener</code>
       
  1964      * implementation.
       
  1965      *
       
  1966      * @return the new <code>ChangeListener</code>
       
  1967      */
       
  1968     protected ChangeListener createChangeListener() {
       
  1969         return getHandler();
       
  1970     }
       
  1971 
       
  1972     /**
       
  1973      * Extends <code>ChangeListener</code> to be serializable.
       
  1974      * <p>
       
  1975      * <strong>Warning:</strong>
       
  1976      * Serialized objects of this class will not be compatible with
       
  1977      * future Swing releases. The current serialization support is
       
  1978      * appropriate for short term storage or RMI between applications running
       
  1979      * the same version of Swing.  As of 1.4, support for long term storage
       
  1980      * of all JavaBeans&trade;
       
  1981      * has been added to the <code>java.beans</code> package.
       
  1982      * Please see {@link java.beans.XMLEncoder}.
       
  1983      */
       
  1984     @SuppressWarnings("serial")
       
  1985     protected class ButtonChangeListener implements ChangeListener, Serializable {
       
  1986         // NOTE: This class is NOT used, instead the functionality has
       
  1987         // been moved to Handler.
       
  1988         ButtonChangeListener() {
       
  1989         }
       
  1990 
       
  1991         public void stateChanged(ChangeEvent e) {
       
  1992             getHandler().stateChanged(e);
       
  1993         }
       
  1994     }
       
  1995 
       
  1996 
       
  1997     /**
       
  1998      * Notifies all listeners that have registered interest for
       
  1999      * notification on this event type.  The event instance
       
  2000      * is lazily created using the <code>event</code>
       
  2001      * parameter.
       
  2002      *
       
  2003      * @param event  the <code>ActionEvent</code> object
       
  2004      * @see EventListenerList
       
  2005      */
       
  2006     protected void fireActionPerformed(ActionEvent event) {
       
  2007         // Guaranteed to return a non-null array
       
  2008         Object[] listeners = listenerList.getListenerList();
       
  2009         ActionEvent e = null;
       
  2010         // Process the listeners last to first, notifying
       
  2011         // those that are interested in this event
       
  2012         for (int i = listeners.length-2; i>=0; i-=2) {
       
  2013             if (listeners[i]==ActionListener.class) {
       
  2014                 // Lazily create the event:
       
  2015                 if (e == null) {
       
  2016                       String actionCommand = event.getActionCommand();
       
  2017                       if(actionCommand == null) {
       
  2018                          actionCommand = getActionCommand();
       
  2019                       }
       
  2020                       e = new ActionEvent(AbstractButton.this,
       
  2021                                           ActionEvent.ACTION_PERFORMED,
       
  2022                                           actionCommand,
       
  2023                                           event.getWhen(),
       
  2024                                           event.getModifiers());
       
  2025                 }
       
  2026                 ((ActionListener)listeners[i+1]).actionPerformed(e);
       
  2027             }
       
  2028         }
       
  2029     }
       
  2030 
       
  2031     /**
       
  2032      * Notifies all listeners that have registered interest for
       
  2033      * notification on this event type.  The event instance
       
  2034      * is lazily created using the <code>event</code> parameter.
       
  2035      *
       
  2036      * @param event  the <code>ItemEvent</code> object
       
  2037      * @see EventListenerList
       
  2038      */
       
  2039     protected void fireItemStateChanged(ItemEvent event) {
       
  2040         // Guaranteed to return a non-null array
       
  2041         Object[] listeners = listenerList.getListenerList();
       
  2042         ItemEvent e = null;
       
  2043         // Process the listeners last to first, notifying
       
  2044         // those that are interested in this event
       
  2045         for (int i = listeners.length-2; i>=0; i-=2) {
       
  2046             if (listeners[i]==ItemListener.class) {
       
  2047                 // Lazily create the event:
       
  2048                 if (e == null) {
       
  2049                     e = new ItemEvent(AbstractButton.this,
       
  2050                                       ItemEvent.ITEM_STATE_CHANGED,
       
  2051                                       AbstractButton.this,
       
  2052                                       event.getStateChange());
       
  2053                 }
       
  2054                 ((ItemListener)listeners[i+1]).itemStateChanged(e);
       
  2055             }
       
  2056         }
       
  2057         if (accessibleContext != null) {
       
  2058             if (event.getStateChange() == ItemEvent.SELECTED) {
       
  2059                 accessibleContext.firePropertyChange(
       
  2060                     AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
       
  2061                     null, AccessibleState.SELECTED);
       
  2062                 accessibleContext.firePropertyChange(
       
  2063                     AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
       
  2064                     Integer.valueOf(0), Integer.valueOf(1));
       
  2065             } else {
       
  2066                 accessibleContext.firePropertyChange(
       
  2067                     AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
       
  2068                     AccessibleState.SELECTED, null);
       
  2069                 accessibleContext.firePropertyChange(
       
  2070                     AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
       
  2071                     Integer.valueOf(1), Integer.valueOf(0));
       
  2072             }
       
  2073         }
       
  2074     }
       
  2075 
       
  2076     /**
       
  2077      * Returns {@code ActionListener} that is added to model.
       
  2078      *
       
  2079      * @return the {@code ActionListener}
       
  2080      */
       
  2081     protected ActionListener createActionListener() {
       
  2082         return getHandler();
       
  2083     }
       
  2084 
       
  2085     /**
       
  2086      * Returns {@code ItemListener} that is added to model.
       
  2087      *
       
  2088      * @return the {@code ItemListener}
       
  2089      */
       
  2090     protected ItemListener createItemListener() {
       
  2091         return getHandler();
       
  2092     }
       
  2093 
       
  2094 
       
  2095     /**
       
  2096      * Enables (or disables) the button.
       
  2097      * @param b  true to enable the button, otherwise false
       
  2098      */
       
  2099     public void setEnabled(boolean b) {
       
  2100         if (!b && model.isRollover()) {
       
  2101             model.setRollover(false);
       
  2102         }
       
  2103         super.setEnabled(b);
       
  2104         model.setEnabled(b);
       
  2105     }
       
  2106 
       
  2107     // *** Deprecated java.awt.Button APIs below *** //
       
  2108 
       
  2109     /**
       
  2110      * Returns the label text.
       
  2111      *
       
  2112      * @return a <code>String</code> containing the label
       
  2113      * @deprecated - Replaced by <code>getText</code>
       
  2114      */
       
  2115     @Deprecated
       
  2116     public String getLabel() {
       
  2117         return getText();
       
  2118     }
       
  2119 
       
  2120     /**
       
  2121      * Sets the label text.
       
  2122      *
       
  2123      * @param label  a <code>String</code> containing the text
       
  2124      * @deprecated - Replaced by <code>setText(text)</code>
       
  2125      * @beaninfo
       
  2126      *        bound: true
       
  2127      *  description: Replace by setText(text)
       
  2128      */
       
  2129     @Deprecated
       
  2130     public void setLabel(String label) {
       
  2131         setText(label);
       
  2132     }
       
  2133 
       
  2134     /**
       
  2135      * Adds an <code>ItemListener</code> to the <code>checkbox</code>.
       
  2136      * @param l  the <code>ItemListener</code> to be added
       
  2137      */
       
  2138     public void addItemListener(ItemListener l) {
       
  2139         listenerList.add(ItemListener.class, l);
       
  2140     }
       
  2141 
       
  2142     /**
       
  2143      * Removes an <code>ItemListener</code> from the button.
       
  2144      * @param l the <code>ItemListener</code> to be removed
       
  2145      */
       
  2146     public void removeItemListener(ItemListener l) {
       
  2147         listenerList.remove(ItemListener.class, l);
       
  2148     }
       
  2149 
       
  2150     /**
       
  2151      * Returns an array of all the <code>ItemListener</code>s added
       
  2152      * to this AbstractButton with addItemListener().
       
  2153      *
       
  2154      * @return all of the <code>ItemListener</code>s added or an empty
       
  2155      *         array if no listeners have been added
       
  2156      * @since 1.4
       
  2157      */
       
  2158     public ItemListener[] getItemListeners() {
       
  2159         return listenerList.getListeners(ItemListener.class);
       
  2160     }
       
  2161 
       
  2162     /**
       
  2163      * Returns an array (length 1) containing the label or
       
  2164      * <code>null</code> if the button is not selected.
       
  2165      *
       
  2166      * @return an array containing 1 Object: the text of the button,
       
  2167      *         if the item is selected; otherwise <code>null</code>
       
  2168      */
       
  2169     public Object[] getSelectedObjects() {
       
  2170         if (isSelected() == false) {
       
  2171             return null;
       
  2172         }
       
  2173         Object[] selectedObjects = new Object[1];
       
  2174         selectedObjects[0] = getText();
       
  2175         return selectedObjects;
       
  2176     }
       
  2177 
       
  2178     /**
       
  2179      * Initialization of the {@code AbstractButton}.
       
  2180      *
       
  2181      * @param text  the text of the button
       
  2182      * @param icon  the Icon image to display on the button
       
  2183      */
       
  2184     protected void init(String text, Icon icon) {
       
  2185         if(text != null) {
       
  2186             setText(text);
       
  2187         }
       
  2188 
       
  2189         if(icon != null) {
       
  2190             setIcon(icon);
       
  2191         }
       
  2192 
       
  2193         // Set the UI
       
  2194         updateUI();
       
  2195 
       
  2196         setAlignmentX(LEFT_ALIGNMENT);
       
  2197         setAlignmentY(CENTER_ALIGNMENT);
       
  2198     }
       
  2199 
       
  2200 
       
  2201     /**
       
  2202      * This is overridden to return false if the current <code>Icon</code>'s
       
  2203      * <code>Image</code> is not equal to the
       
  2204      * passed in <code>Image</code> <code>img</code>.
       
  2205      *
       
  2206      * @param img  the <code>Image</code> to be compared
       
  2207      * @param infoflags flags used to repaint the button when the image
       
  2208      *          is updated and which determine how much is to be painted
       
  2209      * @param x  the x coordinate
       
  2210      * @param y  the y coordinate
       
  2211      * @param w  the width
       
  2212      * @param h  the height
       
  2213      * @see     java.awt.image.ImageObserver
       
  2214      * @see     java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
       
  2215      */
       
  2216     public boolean imageUpdate(Image img, int infoflags,
       
  2217                                int x, int y, int w, int h) {
       
  2218         Icon iconDisplayed = getIcon();
       
  2219         if (iconDisplayed == null) {
       
  2220             return false;
       
  2221         }
       
  2222 
       
  2223         if (!model.isEnabled()) {
       
  2224             if (model.isSelected()) {
       
  2225                 iconDisplayed = getDisabledSelectedIcon();
       
  2226             } else {
       
  2227                 iconDisplayed = getDisabledIcon();
       
  2228             }
       
  2229         } else if (model.isPressed() && model.isArmed()) {
       
  2230             iconDisplayed = getPressedIcon();
       
  2231         } else if (isRolloverEnabled() && model.isRollover()) {
       
  2232             if (model.isSelected()) {
       
  2233                 iconDisplayed = getRolloverSelectedIcon();
       
  2234             } else {
       
  2235                 iconDisplayed = getRolloverIcon();
       
  2236             }
       
  2237         } else if (model.isSelected()) {
       
  2238             iconDisplayed = getSelectedIcon();
       
  2239         }
       
  2240 
       
  2241         if (!SwingUtilities.doesIconReferenceImage(iconDisplayed, img)) {
       
  2242             // We don't know about this image, disable the notification so
       
  2243             // we don't keep repainting.
       
  2244             return false;
       
  2245         }
       
  2246         return super.imageUpdate(img, infoflags, x, y, w, h);
       
  2247     }
       
  2248 
       
  2249     void setUIProperty(String propertyName, Object value) {
       
  2250         if (propertyName == "borderPainted") {
       
  2251             if (!borderPaintedSet) {
       
  2252                 setBorderPainted(((Boolean)value).booleanValue());
       
  2253                 borderPaintedSet = false;
       
  2254             }
       
  2255         } else if (propertyName == "rolloverEnabled") {
       
  2256             if (!rolloverEnabledSet) {
       
  2257                 setRolloverEnabled(((Boolean)value).booleanValue());
       
  2258                 rolloverEnabledSet = false;
       
  2259             }
       
  2260         } else if (propertyName == "iconTextGap") {
       
  2261             if (!iconTextGapSet) {
       
  2262                 setIconTextGap(((Number)value).intValue());
       
  2263                 iconTextGapSet = false;
       
  2264             }
       
  2265         } else if (propertyName == "contentAreaFilled") {
       
  2266             if (!contentAreaFilledSet) {
       
  2267                 setContentAreaFilled(((Boolean)value).booleanValue());
       
  2268                 contentAreaFilledSet = false;
       
  2269             }
       
  2270         } else {
       
  2271             super.setUIProperty(propertyName, value);
       
  2272         }
       
  2273     }
       
  2274 
       
  2275     /**
       
  2276      * Returns a string representation of this <code>AbstractButton</code>.
       
  2277      * This method
       
  2278      * is intended to be used only for debugging purposes, and the
       
  2279      * content and format of the returned string may vary between
       
  2280      * implementations. The returned string may be empty but may not
       
  2281      * be <code>null</code>.
       
  2282      * <P>
       
  2283      * Overriding <code>paramString</code> to provide information about the
       
  2284      * specific new aspects of the JFC components.
       
  2285      *
       
  2286      * @return  a string representation of this <code>AbstractButton</code>
       
  2287      */
       
  2288     protected String paramString() {
       
  2289         String defaultIconString = ((defaultIcon != null)
       
  2290                                     && (defaultIcon != this) ?
       
  2291                                     defaultIcon.toString() : "");
       
  2292         String pressedIconString = ((pressedIcon != null)
       
  2293                                     && (pressedIcon != this) ?
       
  2294                                     pressedIcon.toString() : "");
       
  2295         String disabledIconString = ((disabledIcon != null)
       
  2296                                      && (disabledIcon != this) ?
       
  2297                                      disabledIcon.toString() : "");
       
  2298         String selectedIconString = ((selectedIcon != null)
       
  2299                                      && (selectedIcon != this) ?
       
  2300                                      selectedIcon.toString() : "");
       
  2301         String disabledSelectedIconString = ((disabledSelectedIcon != null) &&
       
  2302                                              (disabledSelectedIcon != this) ?
       
  2303                                              disabledSelectedIcon.toString()
       
  2304                                              : "");
       
  2305         String rolloverIconString = ((rolloverIcon != null)
       
  2306                                      && (rolloverIcon != this) ?
       
  2307                                      rolloverIcon.toString() : "");
       
  2308         String rolloverSelectedIconString = ((rolloverSelectedIcon != null) &&
       
  2309                                              (rolloverSelectedIcon != this) ?
       
  2310                                              rolloverSelectedIcon.toString()
       
  2311                                              : "");
       
  2312         String paintBorderString = (paintBorder ? "true" : "false");
       
  2313         String paintFocusString = (paintFocus ? "true" : "false");
       
  2314         String rolloverEnabledString = (rolloverEnabled ? "true" : "false");
       
  2315 
       
  2316         return super.paramString() +
       
  2317         ",defaultIcon=" + defaultIconString +
       
  2318         ",disabledIcon=" + disabledIconString +
       
  2319         ",disabledSelectedIcon=" + disabledSelectedIconString +
       
  2320         ",margin=" + margin +
       
  2321         ",paintBorder=" + paintBorderString +
       
  2322         ",paintFocus=" + paintFocusString +
       
  2323         ",pressedIcon=" + pressedIconString +
       
  2324         ",rolloverEnabled=" + rolloverEnabledString +
       
  2325         ",rolloverIcon=" + rolloverIconString +
       
  2326         ",rolloverSelectedIcon=" + rolloverSelectedIconString +
       
  2327         ",selectedIcon=" + selectedIconString +
       
  2328         ",text=" + text;
       
  2329     }
       
  2330 
       
  2331 
       
  2332     private Handler getHandler() {
       
  2333         if (handler == null) {
       
  2334             handler = new Handler();
       
  2335         }
       
  2336         return handler;
       
  2337     }
       
  2338 
       
  2339 
       
  2340     //
       
  2341     // Listeners that are added to model
       
  2342     //
       
  2343     @SuppressWarnings("serial")
       
  2344     class Handler implements ActionListener, ChangeListener, ItemListener,
       
  2345                              Serializable {
       
  2346         //
       
  2347         // ChangeListener
       
  2348         //
       
  2349         public void stateChanged(ChangeEvent e) {
       
  2350             Object source = e.getSource();
       
  2351 
       
  2352             updateMnemonicProperties();
       
  2353             if (isEnabled() != model.isEnabled()) {
       
  2354                 setEnabled(model.isEnabled());
       
  2355             }
       
  2356             fireStateChanged();
       
  2357             repaint();
       
  2358         }
       
  2359 
       
  2360         //
       
  2361         // ActionListener
       
  2362         //
       
  2363         public void actionPerformed(ActionEvent event) {
       
  2364             fireActionPerformed(event);
       
  2365         }
       
  2366 
       
  2367         //
       
  2368         // ItemListener
       
  2369         //
       
  2370         public void itemStateChanged(ItemEvent event) {
       
  2371             fireItemStateChanged(event);
       
  2372             if (shouldUpdateSelectedStateFromAction()) {
       
  2373                 Action action = getAction();
       
  2374                 if (action != null && AbstractAction.hasSelectedKey(action)) {
       
  2375                     boolean selected = isSelected();
       
  2376                     boolean isActionSelected = AbstractAction.isSelected(
       
  2377                               action);
       
  2378                     if (isActionSelected != selected) {
       
  2379                         action.putValue(Action.SELECTED_KEY, selected);
       
  2380                     }
       
  2381                 }
       
  2382             }
       
  2383         }
       
  2384     }
       
  2385 
       
  2386 ///////////////////
       
  2387 // Accessibility support
       
  2388 ///////////////////
       
  2389     /**
       
  2390      * This class implements accessibility support for the
       
  2391      * <code>AbstractButton</code> class.  It provides an implementation of the
       
  2392      * Java Accessibility API appropriate to button and menu item
       
  2393      * user-interface elements.
       
  2394      * <p>
       
  2395      * <strong>Warning:</strong>
       
  2396      * Serialized objects of this class will not be compatible with
       
  2397      * future Swing releases. The current serialization support is
       
  2398      * appropriate for short term storage or RMI between applications running
       
  2399      * the same version of Swing.  As of 1.4, support for long term storage
       
  2400      * of all JavaBeans&trade;
       
  2401      * has been added to the <code>java.beans</code> package.
       
  2402      * Please see {@link java.beans.XMLEncoder}.
       
  2403      * @since 1.4
       
  2404      */
       
  2405     @SuppressWarnings("serial") // Same-version serialization only
       
  2406     protected abstract class AccessibleAbstractButton
       
  2407         extends AccessibleJComponent implements AccessibleAction,
       
  2408         AccessibleValue, AccessibleText, AccessibleExtendedComponent {
       
  2409 
       
  2410         /**
       
  2411          * Returns the accessible name of this object.
       
  2412          *
       
  2413          * @return the localized name of the object -- can be
       
  2414          *              <code>null</code> if this
       
  2415          *              object does not have a name
       
  2416          */
       
  2417         public String getAccessibleName() {
       
  2418             String name = accessibleName;
       
  2419 
       
  2420             if (name == null) {
       
  2421                 name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
       
  2422             }
       
  2423             if (name == null) {
       
  2424                 name = AbstractButton.this.getText();
       
  2425             }
       
  2426             if (name == null) {
       
  2427                 name = super.getAccessibleName();
       
  2428             }
       
  2429             return name;
       
  2430         }
       
  2431 
       
  2432         /**
       
  2433          * Get the AccessibleIcons associated with this object if one
       
  2434          * or more exist.  Otherwise return null.
       
  2435          * @since 1.3
       
  2436          */
       
  2437         public AccessibleIcon [] getAccessibleIcon() {
       
  2438             Icon defaultIcon = getIcon();
       
  2439 
       
  2440             if (defaultIcon instanceof Accessible) {
       
  2441                 AccessibleContext ac =
       
  2442                     ((Accessible)defaultIcon).getAccessibleContext();
       
  2443                 if (ac != null && ac instanceof AccessibleIcon) {
       
  2444                     return new AccessibleIcon[] { (AccessibleIcon)ac };
       
  2445                 }
       
  2446             }
       
  2447             return null;
       
  2448         }
       
  2449 
       
  2450         /**
       
  2451          * Get the state set of this object.
       
  2452          *
       
  2453          * @return an instance of AccessibleState containing the current state
       
  2454          * of the object
       
  2455          * @see AccessibleState
       
  2456          */
       
  2457         public AccessibleStateSet getAccessibleStateSet() {
       
  2458         AccessibleStateSet states = super.getAccessibleStateSet();
       
  2459             if (getModel().isArmed()) {
       
  2460                 states.add(AccessibleState.ARMED);
       
  2461             }
       
  2462             if (isFocusOwner()) {
       
  2463                 states.add(AccessibleState.FOCUSED);
       
  2464             }
       
  2465             if (getModel().isPressed()) {
       
  2466                 states.add(AccessibleState.PRESSED);
       
  2467             }
       
  2468             if (isSelected()) {
       
  2469                 states.add(AccessibleState.CHECKED);
       
  2470             }
       
  2471             return states;
       
  2472         }
       
  2473 
       
  2474         /**
       
  2475          * Get the AccessibleRelationSet associated with this object if one
       
  2476          * exists.  Otherwise return null.
       
  2477          * @see AccessibleRelation
       
  2478          * @since 1.3
       
  2479          */
       
  2480         public AccessibleRelationSet getAccessibleRelationSet() {
       
  2481 
       
  2482             // Check where the AccessibleContext's relation
       
  2483             // set already contains a MEMBER_OF relation.
       
  2484             AccessibleRelationSet relationSet
       
  2485                 = super.getAccessibleRelationSet();
       
  2486 
       
  2487             if (!relationSet.contains(AccessibleRelation.MEMBER_OF)) {
       
  2488                 // get the members of the button group if one exists
       
  2489                 ButtonModel model = getModel();
       
  2490                 if (model != null && model instanceof DefaultButtonModel) {
       
  2491                     ButtonGroup group = ((DefaultButtonModel)model).getGroup();
       
  2492                     if (group != null) {
       
  2493                         // set the target of the MEMBER_OF relation to be
       
  2494                         // the members of the button group.
       
  2495                         int len = group.getButtonCount();
       
  2496                         Object [] target = new Object[len];
       
  2497                         Enumeration<AbstractButton> elem = group.getElements();
       
  2498                         for (int i = 0; i < len; i++) {
       
  2499                             if (elem.hasMoreElements()) {
       
  2500                                 target[i] = elem.nextElement();
       
  2501                             }
       
  2502                         }
       
  2503                         AccessibleRelation relation =
       
  2504                             new AccessibleRelation(AccessibleRelation.MEMBER_OF);
       
  2505                         relation.setTarget(target);
       
  2506                         relationSet.add(relation);
       
  2507                     }
       
  2508                 }
       
  2509             }
       
  2510             return relationSet;
       
  2511         }
       
  2512 
       
  2513         /**
       
  2514          * Get the AccessibleAction associated with this object.  In the
       
  2515          * implementation of the Java Accessibility API for this class,
       
  2516          * return this object, which is responsible for implementing the
       
  2517          * AccessibleAction interface on behalf of itself.
       
  2518          *
       
  2519          * @return this object
       
  2520          */
       
  2521         public AccessibleAction getAccessibleAction() {
       
  2522             return this;
       
  2523         }
       
  2524 
       
  2525         /**
       
  2526          * Get the AccessibleValue associated with this object.  In the
       
  2527          * implementation of the Java Accessibility API for this class,
       
  2528          * return this object, which is responsible for implementing the
       
  2529          * AccessibleValue interface on behalf of itself.
       
  2530          *
       
  2531          * @return this object
       
  2532          */
       
  2533         public AccessibleValue getAccessibleValue() {
       
  2534             return this;
       
  2535         }
       
  2536 
       
  2537         /**
       
  2538          * Returns the number of Actions available in this object.  The
       
  2539          * default behavior of a button is to have one action - toggle
       
  2540          * the button.
       
  2541          *
       
  2542          * @return 1, the number of Actions in this object
       
  2543          */
       
  2544         public int getAccessibleActionCount() {
       
  2545             return 1;
       
  2546         }
       
  2547 
       
  2548         /**
       
  2549          * Return a description of the specified action of the object.
       
  2550          *
       
  2551          * @param i zero-based index of the actions
       
  2552          */
       
  2553         public String getAccessibleActionDescription(int i) {
       
  2554             if (i == 0) {
       
  2555                 return UIManager.getString("AbstractButton.clickText");
       
  2556             } else {
       
  2557                 return null;
       
  2558             }
       
  2559         }
       
  2560 
       
  2561         /**
       
  2562          * Perform the specified Action on the object
       
  2563          *
       
  2564          * @param i zero-based index of actions
       
  2565          * @return true if the the action was performed; else false.
       
  2566          */
       
  2567         public boolean doAccessibleAction(int i) {
       
  2568             if (i == 0) {
       
  2569                 doClick();
       
  2570                 return true;
       
  2571             } else {
       
  2572                 return false;
       
  2573             }
       
  2574         }
       
  2575 
       
  2576         /**
       
  2577          * Get the value of this object as a Number.
       
  2578          *
       
  2579          * @return An Integer of 0 if this isn't selected or an Integer of 1 if
       
  2580          * this is selected.
       
  2581          * @see AbstractButton#isSelected
       
  2582          */
       
  2583         public Number getCurrentAccessibleValue() {
       
  2584             if (isSelected()) {
       
  2585                 return Integer.valueOf(1);
       
  2586             } else {
       
  2587                 return Integer.valueOf(0);
       
  2588             }
       
  2589         }
       
  2590 
       
  2591         /**
       
  2592          * Set the value of this object as a Number.
       
  2593          *
       
  2594          * @return True if the value was set.
       
  2595          */
       
  2596         public boolean setCurrentAccessibleValue(Number n) {
       
  2597             // TIGER - 4422535
       
  2598             if (n == null) {
       
  2599                 return false;
       
  2600             }
       
  2601             int i = n.intValue();
       
  2602             if (i == 0) {
       
  2603                 setSelected(false);
       
  2604             } else {
       
  2605                 setSelected(true);
       
  2606             }
       
  2607             return true;
       
  2608         }
       
  2609 
       
  2610         /**
       
  2611          * Get the minimum value of this object as a Number.
       
  2612          *
       
  2613          * @return an Integer of 0.
       
  2614          */
       
  2615         public Number getMinimumAccessibleValue() {
       
  2616             return Integer.valueOf(0);
       
  2617         }
       
  2618 
       
  2619         /**
       
  2620          * Get the maximum value of this object as a Number.
       
  2621          *
       
  2622          * @return An Integer of 1.
       
  2623          */
       
  2624         public Number getMaximumAccessibleValue() {
       
  2625             return Integer.valueOf(1);
       
  2626         }
       
  2627 
       
  2628 
       
  2629         /* AccessibleText ---------- */
       
  2630 
       
  2631         public AccessibleText getAccessibleText() {
       
  2632             View view = (View)AbstractButton.this.getClientProperty("html");
       
  2633             if (view != null) {
       
  2634                 return this;
       
  2635             } else {
       
  2636                 return null;
       
  2637             }
       
  2638         }
       
  2639 
       
  2640         /**
       
  2641          * Given a point in local coordinates, return the zero-based index
       
  2642          * of the character under that Point.  If the point is invalid,
       
  2643          * this method returns -1.
       
  2644          *
       
  2645          * Note: the AbstractButton must have a valid size (e.g. have
       
  2646          * been added to a parent container whose ancestor container
       
  2647          * is a valid top-level window) for this method to be able
       
  2648          * to return a meaningful value.
       
  2649          *
       
  2650          * @param p the Point in local coordinates
       
  2651          * @return the zero-based index of the character under Point p; if
       
  2652          * Point is invalid returns -1.
       
  2653          * @since 1.3
       
  2654          */
       
  2655         public int getIndexAtPoint(Point p) {
       
  2656             View view = (View) AbstractButton.this.getClientProperty("html");
       
  2657             if (view != null) {
       
  2658                 Rectangle r = getTextRectangle();
       
  2659                 if (r == null) {
       
  2660                     return -1;
       
  2661                 }
       
  2662                 Rectangle2D.Float shape =
       
  2663                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
       
  2664                 Position.Bias bias[] = new Position.Bias[1];
       
  2665                 return view.viewToModel(p.x, p.y, shape, bias);
       
  2666             } else {
       
  2667                 return -1;
       
  2668             }
       
  2669         }
       
  2670 
       
  2671         /**
       
  2672          * Determine the bounding box of the character at the given
       
  2673          * index into the string.  The bounds are returned in local
       
  2674          * coordinates.  If the index is invalid an empty rectangle is
       
  2675          * returned.
       
  2676          *
       
  2677          * Note: the AbstractButton must have a valid size (e.g. have
       
  2678          * been added to a parent container whose ancestor container
       
  2679          * is a valid top-level window) for this method to be able
       
  2680          * to return a meaningful value.
       
  2681          *
       
  2682          * @param i the index into the String
       
  2683          * @return the screen coordinates of the character's the bounding box,
       
  2684          * if index is invalid returns an empty rectangle.
       
  2685          * @since 1.3
       
  2686          */
       
  2687         public Rectangle getCharacterBounds(int i) {
       
  2688             View view = (View) AbstractButton.this.getClientProperty("html");
       
  2689             if (view != null) {
       
  2690                 Rectangle r = getTextRectangle();
       
  2691                 if (r == null) {
       
  2692                     return null;
       
  2693                 }
       
  2694                 Rectangle2D.Float shape =
       
  2695                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
       
  2696                 try {
       
  2697                     Shape charShape =
       
  2698                         view.modelToView(i, shape, Position.Bias.Forward);
       
  2699                     return charShape.getBounds();
       
  2700                 } catch (BadLocationException e) {
       
  2701                     return null;
       
  2702                 }
       
  2703             } else {
       
  2704                 return null;
       
  2705             }
       
  2706         }
       
  2707 
       
  2708         /**
       
  2709          * Return the number of characters (valid indicies)
       
  2710          *
       
  2711          * @return the number of characters
       
  2712          * @since 1.3
       
  2713          */
       
  2714         public int getCharCount() {
       
  2715             View view = (View) AbstractButton.this.getClientProperty("html");
       
  2716             if (view != null) {
       
  2717                 Document d = view.getDocument();
       
  2718                 if (d instanceof StyledDocument) {
       
  2719                     StyledDocument doc = (StyledDocument)d;
       
  2720                     return doc.getLength();
       
  2721                 }
       
  2722             }
       
  2723             return accessibleContext.getAccessibleName().length();
       
  2724         }
       
  2725 
       
  2726         /**
       
  2727          * Return the zero-based offset of the caret.
       
  2728          *
       
  2729          * Note: That to the right of the caret will have the same index
       
  2730          * value as the offset (the caret is between two characters).
       
  2731          * @return the zero-based offset of the caret.
       
  2732          * @since 1.3
       
  2733          */
       
  2734         public int getCaretPosition() {
       
  2735             // There is no caret.
       
  2736             return -1;
       
  2737         }
       
  2738 
       
  2739         /**
       
  2740          * Returns the String at a given index.
       
  2741          *
       
  2742          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
       
  2743          * or AccessibleText.SENTENCE to retrieve
       
  2744          * @param index an index within the text &gt;= 0
       
  2745          * @return the letter, word, or sentence,
       
  2746          *   null for an invalid index or part
       
  2747          * @since 1.3
       
  2748          */
       
  2749         public String getAtIndex(int part, int index) {
       
  2750             if (index < 0 || index >= getCharCount()) {
       
  2751                 return null;
       
  2752             }
       
  2753             switch (part) {
       
  2754             case AccessibleText.CHARACTER:
       
  2755                 try {
       
  2756                     return getText(index, 1);
       
  2757                 } catch (BadLocationException e) {
       
  2758                     return null;
       
  2759                 }
       
  2760             case AccessibleText.WORD:
       
  2761                 try {
       
  2762                     String s = getText(0, getCharCount());
       
  2763                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
       
  2764                     words.setText(s);
       
  2765                     int end = words.following(index);
       
  2766                     return s.substring(words.previous(), end);
       
  2767                 } catch (BadLocationException e) {
       
  2768                     return null;
       
  2769                 }
       
  2770             case AccessibleText.SENTENCE:
       
  2771                 try {
       
  2772                     String s = getText(0, getCharCount());
       
  2773                     BreakIterator sentence =
       
  2774                         BreakIterator.getSentenceInstance(getLocale());
       
  2775                     sentence.setText(s);
       
  2776                     int end = sentence.following(index);
       
  2777                     return s.substring(sentence.previous(), end);
       
  2778                 } catch (BadLocationException e) {
       
  2779                     return null;
       
  2780                 }
       
  2781             default:
       
  2782                 return null;
       
  2783             }
       
  2784         }
       
  2785 
       
  2786         /**
       
  2787          * Returns the String after a given index.
       
  2788          *
       
  2789          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
       
  2790          * or AccessibleText.SENTENCE to retrieve
       
  2791          * @param index an index within the text &gt;= 0
       
  2792          * @return the letter, word, or sentence, null for an invalid
       
  2793          *  index or part
       
  2794          * @since 1.3
       
  2795          */
       
  2796         public String getAfterIndex(int part, int index) {
       
  2797             if (index < 0 || index >= getCharCount()) {
       
  2798                 return null;
       
  2799             }
       
  2800             switch (part) {
       
  2801             case AccessibleText.CHARACTER:
       
  2802                 if (index+1 >= getCharCount()) {
       
  2803                    return null;
       
  2804                 }
       
  2805                 try {
       
  2806                     return getText(index+1, 1);
       
  2807                 } catch (BadLocationException e) {
       
  2808                     return null;
       
  2809                 }
       
  2810             case AccessibleText.WORD:
       
  2811                 try {
       
  2812                     String s = getText(0, getCharCount());
       
  2813                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
       
  2814                     words.setText(s);
       
  2815                     int start = words.following(index);
       
  2816                     if (start == BreakIterator.DONE || start >= s.length()) {
       
  2817                         return null;
       
  2818                     }
       
  2819                     int end = words.following(start);
       
  2820                     if (end == BreakIterator.DONE || end >= s.length()) {
       
  2821                         return null;
       
  2822                     }
       
  2823                     return s.substring(start, end);
       
  2824                 } catch (BadLocationException e) {
       
  2825                     return null;
       
  2826                 }
       
  2827             case AccessibleText.SENTENCE:
       
  2828                 try {
       
  2829                     String s = getText(0, getCharCount());
       
  2830                     BreakIterator sentence =
       
  2831                         BreakIterator.getSentenceInstance(getLocale());
       
  2832                     sentence.setText(s);
       
  2833                     int start = sentence.following(index);
       
  2834                     if (start == BreakIterator.DONE || start > s.length()) {
       
  2835                         return null;
       
  2836                     }
       
  2837                     int end = sentence.following(start);
       
  2838                     if (end == BreakIterator.DONE || end > s.length()) {
       
  2839                         return null;
       
  2840                     }
       
  2841                     return s.substring(start, end);
       
  2842                 } catch (BadLocationException e) {
       
  2843                     return null;
       
  2844                 }
       
  2845             default:
       
  2846                 return null;
       
  2847             }
       
  2848         }
       
  2849 
       
  2850         /**
       
  2851          * Returns the String before a given index.
       
  2852          *
       
  2853          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
       
  2854          *   or AccessibleText.SENTENCE to retrieve
       
  2855          * @param index an index within the text &gt;= 0
       
  2856          * @return the letter, word, or sentence, null for an invalid index
       
  2857          *  or part
       
  2858          * @since 1.3
       
  2859          */
       
  2860         public String getBeforeIndex(int part, int index) {
       
  2861             if (index < 0 || index > getCharCount()-1) {
       
  2862                 return null;
       
  2863             }
       
  2864             switch (part) {
       
  2865             case AccessibleText.CHARACTER:
       
  2866                 if (index == 0) {
       
  2867                     return null;
       
  2868                 }
       
  2869                 try {
       
  2870                     return getText(index-1, 1);
       
  2871                 } catch (BadLocationException e) {
       
  2872                     return null;
       
  2873                 }
       
  2874             case AccessibleText.WORD:
       
  2875                 try {
       
  2876                     String s = getText(0, getCharCount());
       
  2877                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
       
  2878                     words.setText(s);
       
  2879                     int end = words.following(index);
       
  2880                     end = words.previous();
       
  2881                     int start = words.previous();
       
  2882                     if (start == BreakIterator.DONE) {
       
  2883                         return null;
       
  2884                     }
       
  2885                     return s.substring(start, end);
       
  2886                 } catch (BadLocationException e) {
       
  2887                     return null;
       
  2888                 }
       
  2889             case AccessibleText.SENTENCE:
       
  2890                 try {
       
  2891                     String s = getText(0, getCharCount());
       
  2892                     BreakIterator sentence =
       
  2893                         BreakIterator.getSentenceInstance(getLocale());
       
  2894                     sentence.setText(s);
       
  2895                     int end = sentence.following(index);
       
  2896                     end = sentence.previous();
       
  2897                     int start = sentence.previous();
       
  2898                     if (start == BreakIterator.DONE) {
       
  2899                         return null;
       
  2900                     }
       
  2901                     return s.substring(start, end);
       
  2902                 } catch (BadLocationException e) {
       
  2903                     return null;
       
  2904                 }
       
  2905             default:
       
  2906                 return null;
       
  2907             }
       
  2908         }
       
  2909 
       
  2910         /**
       
  2911          * Return the AttributeSet for a given character at a given index
       
  2912          *
       
  2913          * @param i the zero-based index into the text
       
  2914          * @return the AttributeSet of the character
       
  2915          * @since 1.3
       
  2916          */
       
  2917         public AttributeSet getCharacterAttribute(int i) {
       
  2918             View view = (View) AbstractButton.this.getClientProperty("html");
       
  2919             if (view != null) {
       
  2920                 Document d = view.getDocument();
       
  2921                 if (d instanceof StyledDocument) {
       
  2922                     StyledDocument doc = (StyledDocument)d;
       
  2923                     Element elem = doc.getCharacterElement(i);
       
  2924                     if (elem != null) {
       
  2925                         return elem.getAttributes();
       
  2926                     }
       
  2927                 }
       
  2928             }
       
  2929             return null;
       
  2930         }
       
  2931 
       
  2932         /**
       
  2933          * Returns the start offset within the selected text.
       
  2934          * If there is no selection, but there is
       
  2935          * a caret, the start and end offsets will be the same.
       
  2936          *
       
  2937          * @return the index into the text of the start of the selection
       
  2938          * @since 1.3
       
  2939          */
       
  2940         public int getSelectionStart() {
       
  2941             // Text cannot be selected.
       
  2942             return -1;
       
  2943         }
       
  2944 
       
  2945         /**
       
  2946          * Returns the end offset within the selected text.
       
  2947          * If there is no selection, but there is
       
  2948          * a caret, the start and end offsets will be the same.
       
  2949          *
       
  2950          * @return the index into the text of the end of the selection
       
  2951          * @since 1.3
       
  2952          */
       
  2953         public int getSelectionEnd() {
       
  2954             // Text cannot be selected.
       
  2955             return -1;
       
  2956         }
       
  2957 
       
  2958         /**
       
  2959          * Returns the portion of the text that is selected.
       
  2960          *
       
  2961          * @return the String portion of the text that is selected
       
  2962          * @since 1.3
       
  2963          */
       
  2964         public String getSelectedText() {
       
  2965             // Text cannot be selected.
       
  2966             return null;
       
  2967         }
       
  2968 
       
  2969         /*
       
  2970          * Returns the text substring starting at the specified
       
  2971          * offset with the specified length.
       
  2972          */
       
  2973         private String getText(int offset, int length)
       
  2974             throws BadLocationException {
       
  2975 
       
  2976             View view = (View) AbstractButton.this.getClientProperty("html");
       
  2977             if (view != null) {
       
  2978                 Document d = view.getDocument();
       
  2979                 if (d instanceof StyledDocument) {
       
  2980                     StyledDocument doc = (StyledDocument)d;
       
  2981                     return doc.getText(offset, length);
       
  2982                 }
       
  2983             }
       
  2984             return null;
       
  2985         }
       
  2986 
       
  2987         /*
       
  2988          * Returns the bounding rectangle for the component text.
       
  2989          */
       
  2990         private Rectangle getTextRectangle() {
       
  2991 
       
  2992             String text = AbstractButton.this.getText();
       
  2993             Icon icon = (AbstractButton.this.isEnabled()) ? AbstractButton.this.getIcon() : AbstractButton.this.getDisabledIcon();
       
  2994 
       
  2995             if ((icon == null) && (text == null)) {
       
  2996                 return null;
       
  2997             }
       
  2998 
       
  2999             Rectangle paintIconR = new Rectangle();
       
  3000             Rectangle paintTextR = new Rectangle();
       
  3001             Rectangle paintViewR = new Rectangle();
       
  3002             Insets paintViewInsets = new Insets(0, 0, 0, 0);
       
  3003 
       
  3004             paintViewInsets = AbstractButton.this.getInsets(paintViewInsets);
       
  3005             paintViewR.x = paintViewInsets.left;
       
  3006             paintViewR.y = paintViewInsets.top;
       
  3007             paintViewR.width = AbstractButton.this.getWidth() - (paintViewInsets.left + paintViewInsets.right);
       
  3008             paintViewR.height = AbstractButton.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
       
  3009 
       
  3010             String clippedText = SwingUtilities.layoutCompoundLabel(
       
  3011                 AbstractButton.this,
       
  3012                 getFontMetrics(getFont()),
       
  3013                 text,
       
  3014                 icon,
       
  3015                 AbstractButton.this.getVerticalAlignment(),
       
  3016                 AbstractButton.this.getHorizontalAlignment(),
       
  3017                 AbstractButton.this.getVerticalTextPosition(),
       
  3018                 AbstractButton.this.getHorizontalTextPosition(),
       
  3019                 paintViewR,
       
  3020                 paintIconR,
       
  3021                 paintTextR,
       
  3022                 0);
       
  3023 
       
  3024             return paintTextR;
       
  3025         }
       
  3026 
       
  3027         // ----- AccessibleExtendedComponent
       
  3028 
       
  3029         /**
       
  3030          * Returns the AccessibleExtendedComponent
       
  3031          *
       
  3032          * @return the AccessibleExtendedComponent
       
  3033          */
       
  3034         AccessibleExtendedComponent getAccessibleExtendedComponent() {
       
  3035             return this;
       
  3036         }
       
  3037 
       
  3038         /**
       
  3039          * Returns the tool tip text
       
  3040          *
       
  3041          * @return the tool tip text, if supported, of the object;
       
  3042          * otherwise, null
       
  3043          * @since 1.4
       
  3044          */
       
  3045         public String getToolTipText() {
       
  3046             return AbstractButton.this.getToolTipText();
       
  3047         }
       
  3048 
       
  3049         /**
       
  3050          * Returns the titled border text
       
  3051          *
       
  3052          * @return the titled border text, if supported, of the object;
       
  3053          * otherwise, null
       
  3054          * @since 1.4
       
  3055          */
       
  3056         public String getTitledBorderText() {
       
  3057             return super.getTitledBorderText();
       
  3058         }
       
  3059 
       
  3060         /**
       
  3061          * Returns key bindings associated with this object
       
  3062          *
       
  3063          * @return the key bindings, if supported, of the object;
       
  3064          * otherwise, null
       
  3065          * @see AccessibleKeyBinding
       
  3066          * @since 1.4
       
  3067          */
       
  3068         public AccessibleKeyBinding getAccessibleKeyBinding() {
       
  3069             int mnemonic = AbstractButton.this.getMnemonic();
       
  3070             if (mnemonic == 0) {
       
  3071                 return null;
       
  3072             }
       
  3073             return new ButtonKeyBinding(mnemonic);
       
  3074         }
       
  3075 
       
  3076         class ButtonKeyBinding implements AccessibleKeyBinding {
       
  3077             int mnemonic;
       
  3078 
       
  3079             ButtonKeyBinding(int mnemonic) {
       
  3080                 this.mnemonic = mnemonic;
       
  3081             }
       
  3082 
       
  3083             /**
       
  3084              * Returns the number of key bindings for this object
       
  3085              *
       
  3086              * @return the zero-based number of key bindings for this object
       
  3087              */
       
  3088             public int getAccessibleKeyBindingCount() {
       
  3089                 return 1;
       
  3090             }
       
  3091 
       
  3092             /**
       
  3093              * Returns a key binding for this object.  The value returned is an
       
  3094              * java.lang.Object which must be cast to appropriate type depending
       
  3095              * on the underlying implementation of the key.  For example, if the
       
  3096              * Object returned is a javax.swing.KeyStroke, the user of this
       
  3097              * method should do the following:
       
  3098              * <nf><code>
       
  3099              * Component c = <get the component that has the key bindings>
       
  3100              * AccessibleContext ac = c.getAccessibleContext();
       
  3101              * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
       
  3102              * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
       
  3103              *     Object o = akb.getAccessibleKeyBinding(i);
       
  3104              *     if (o instanceof javax.swing.KeyStroke) {
       
  3105              *         javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
       
  3106              *         <do something with the key binding>
       
  3107              *     }
       
  3108              * }
       
  3109              * </code></nf>
       
  3110              *
       
  3111              * @param i zero-based index of the key bindings
       
  3112              * @return a javax.lang.Object which specifies the key binding
       
  3113              * @exception IllegalArgumentException if the index is
       
  3114              * out of bounds
       
  3115              * @see #getAccessibleKeyBindingCount
       
  3116              */
       
  3117             public java.lang.Object getAccessibleKeyBinding(int i) {
       
  3118                 if (i != 0) {
       
  3119                     throw new IllegalArgumentException();
       
  3120                 }
       
  3121                 return KeyStroke.getKeyStroke(mnemonic, 0);
       
  3122             }
       
  3123         }
       
  3124     }
       
  3125 }