alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (AbstractButton.java)

This example Java source code file (AbstractButton.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

action, awt, badlocationexception, bean, breakiterator, center, changelistener, event, gui, icon, illegalargumentexception, insets, javabean, object, rectangle, string, styleddocument, uiresource, util, view

The AbstractButton.java Java example source code

/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package javax.swing;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.text.*;
import java.awt.geom.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.Transient;
import java.util.Enumeration;
import java.util.Vector;
import java.io.Serializable;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.accessibility.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.plaf.basic.*;
import java.util.*;

/**
 * Defines common behaviors for buttons and menu items.
 * <p>
 * Buttons can be configured, and to some degree controlled, by
 * <code>Actions.  Using an
 * <code>Action with a button has many benefits beyond directly
 * configuring a button.  Refer to <a href="Action.html#buttonActions">
 * Swing Components Supporting <code>Action for more
 * details, and you can find more information in <a
 * href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How
 * to Use Actions</a>, a section in The Java Tutorial.
 * <p>
 * For further information see
 * <a
 href="http://docs.oracle.com/javase/tutorial/uiswing/components/button.html">How to Use Buttons, Check Boxes, and Radio Buttons</a>,
 * a section in <em>The Java Tutorial.
 * <p>
 * <strong>Warning:
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans™
 * has been added to the <code>java.beans package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @author Jeff Dinkins
 */
public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {

    // *********************************
    // ******* Button properties *******
    // *********************************

    /** Identifies a change in the button model. */
    public static final String MODEL_CHANGED_PROPERTY = "model";
    /** Identifies a change in the button's text. */
    public static final String TEXT_CHANGED_PROPERTY = "text";
    /** Identifies a change to the button's mnemonic. */
    public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";

    // Text positioning and alignment
    /** Identifies a change in the button's margins. */
    public static final String MARGIN_CHANGED_PROPERTY = "margin";
    /** Identifies a change in the button's vertical alignment. */
    public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = "verticalAlignment";
    /** Identifies a change in the button's horizontal alignment. */
    public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = "horizontalAlignment";

    /** Identifies a change in the button's vertical text position. */
    public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = "verticalTextPosition";
    /** Identifies a change in the button's horizontal text position. */
    public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = "horizontalTextPosition";

    // Paint options
    /**
     * Identifies a change to having the border drawn,
     * or having it not drawn.
     */
    public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
    /**
     * Identifies a change to having the border highlighted when focused,
     * or not.
     */
    public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
    /**
     * Identifies a change from rollover enabled to disabled or back
     * to enabled.
     */
    public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = "rolloverEnabled";
    /**
     * Identifies a change to having the button paint the content area.
     */
    public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = "contentAreaFilled";

    // Icons
    /** Identifies a change to the icon that represents the button. */
    public static final String ICON_CHANGED_PROPERTY = "icon";

    /**
     * Identifies a change to the icon used when the button has been
     * pressed.
     */
    public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
    /**
     * Identifies a change to the icon used when the button has
     * been selected.
     */
    public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";

    /**
     * Identifies a change to the icon used when the cursor is over
     * the button.
     */
    public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
    /**
     * Identifies a change to the icon used when the cursor is
     * over the button and it has been selected.
     */
    public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = "rolloverSelectedIcon";

    /**
     * Identifies a change to the icon used when the button has
     * been disabled.
     */
    public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
    /**
     * Identifies a change to the icon used when the button has been
     * disabled and selected.
     */
    public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = "disabledSelectedIcon";


    /** The data model that determines the button's state. */
    protected ButtonModel model                = null;

    private String     text                    = ""; // for BeanBox
    private Insets     margin                  = null;
    private Insets     defaultMargin           = null;

    // Button icons
    // PENDING(jeff) - hold icons in an array
    private Icon       defaultIcon             = null;
    private Icon       pressedIcon             = null;
    private Icon       disabledIcon            = null;

    private Icon       selectedIcon            = null;
    private Icon       disabledSelectedIcon    = null;

    private Icon       rolloverIcon            = null;
    private Icon       rolloverSelectedIcon    = null;

    // Display properties
    private boolean    paintBorder             = true;
    private boolean    paintFocus              = true;
    private boolean    rolloverEnabled         = false;
    private boolean    contentAreaFilled         = true;

    // Icon/Label Alignment
    private int        verticalAlignment       = CENTER;
    private int        horizontalAlignment     = CENTER;

    private int        verticalTextPosition    = CENTER;
    private int        horizontalTextPosition  = TRAILING;

    private int        iconTextGap             = 4;

    private int        mnemonic;
    private int        mnemonicIndex           = -1;

    private long       multiClickThreshhold    = 0;

    private boolean    borderPaintedSet        = false;
    private boolean    rolloverEnabledSet      = false;
    private boolean    iconTextGapSet          = false;
    private boolean    contentAreaFilledSet    = false;

    // Whether or not we've set the LayoutManager.
    private boolean setLayout = false;

    // This is only used by JButton, promoted to avoid an extra
    // boolean field in JButton
    boolean defaultCapable = true;

    /**
     * Combined listeners: ActionListener, ChangeListener, ItemListener.
     */
    private Handler handler;

    /**
     * The button model's <code>changeListener.
     */
    protected ChangeListener changeListener = null;
    /**
     * The button model's <code>ActionListener.
     */
    protected ActionListener actionListener = null;
    /**
     * The button model's <code>ItemListener.
     */
    protected ItemListener itemListener = null;

    /**
     * Only one <code>ChangeEvent is needed per button
     * instance since the
     * event's only state is the source property.  The source of events
     * generated is always "this".
     */
    protected transient ChangeEvent changeEvent;

    private boolean hideActionText = false;

    /**
     * Sets the <code>hideActionText property, which determines
     * whether the button displays text from the <code>Action.
     * This is useful only if an <code>Action has been
     * installed on the button.
     *
     * @param hideActionText <code>true if the button's
     *                       <code>text property should not reflect
     *                       that of the <code>Action; the default is
     *                       <code>false
     * @see <a href="Action.html#buttonActions">Swing Components Supporting
     *      <code>Action
     * @since 1.6
     * @beaninfo
     *        bound: true
     *    expert: true
     *  description: Whether the text of the button should come from
     *               the <code>Action.
     */
    public void setHideActionText(boolean hideActionText) {
        if (hideActionText != this.hideActionText) {
            this.hideActionText = hideActionText;
            if (getAction() != null) {
                setTextFromAction(getAction(), false);
            }
            firePropertyChange("hideActionText", !hideActionText,
                               hideActionText);
        }
    }

    /**
     * Returns the value of the <code>hideActionText property, which
     * determines whether the button displays text from the
     * <code>Action.  This is useful only if an Action
     * has been installed on the button.
     *
     * @return <code>true if the button's text
     *         property should not reflect that of the
     *         <code>Action; the default is false
     * @since 1.6
     */
    public boolean getHideActionText() {
        return hideActionText;
    }

    /**
     * Returns the button's text.
     * @return the buttons text
     * @see #setText
     */
    public String getText() {
        return text;
    }

    /**
     * Sets the button's text.
     * @param text the string used to set the text
     * @see #getText
     * @beaninfo
     *        bound: true
     *    preferred: true
     *    attribute: visualUpdate true
     *  description: The button's text.
     */
    public void setText(String text) {
        String oldValue = this.text;
        this.text = text;
        firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);
        updateDisplayedMnemonicIndex(text, getMnemonic());

        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, text);
        }
        if (text == null || oldValue == null || !text.equals(oldValue)) {
            revalidate();
            repaint();
        }
    }


    /**
     * Returns the state of the button. True if the
     * toggle button is selected, false if it's not.
     * @return true if the toggle button is selected, otherwise false
     */
    public boolean isSelected() {
        return model.isSelected();
    }

    /**
     * Sets the state of the button. Note that this method does not
     * trigger an <code>actionEvent.
     * Call <code>doClick to perform a programmatic action change.
     *
     * @param b  true if the button is selected, otherwise false
     */
    public void setSelected(boolean b) {
        boolean oldValue = isSelected();

        // TIGER - 4840653
        // Removed code which fired an AccessibleState.SELECTED
        // PropertyChangeEvent since this resulted in two
        // identical events being fired since
        // AbstractButton.fireItemStateChanged also fires the
        // same event. This caused screen readers to speak the
        // name of the item twice.

        model.setSelected(b);
    }

    /**
     * Programmatically perform a "click". This does the same
     * thing as if the user had pressed and released the button.
     */
    public void doClick() {
        doClick(68);
    }

    /**
     * Programmatically perform a "click". This does the same
     * thing as if the user had pressed and released the button.
     * The button stays visually "pressed" for <code>pressTime
     *  milliseconds.
     *
     * @param pressTime the time to "hold down" the button, in milliseconds
     */
    public void doClick(int pressTime) {
        Dimension size = getSize();
        model.setArmed(true);
        model.setPressed(true);
        paintImmediately(new Rectangle(0,0, size.width, size.height));
        try {
            Thread.currentThread().sleep(pressTime);
        } catch(InterruptedException ie) {
        }
        model.setPressed(false);
        model.setArmed(false);
    }

    /**
     * Sets space for margin between the button's border and
     * the label. Setting to <code>null will cause the button to
     * use the default margin.  The button's default <code>Border
     * object will use this value to create the proper margin.
     * However, if a non-default border is set on the button,
     * it is that <code>Border object's responsibility to create the
     * appropriate margin space (else this property will
     * effectively be ignored).
     *
     * @param m the space between the border and the label
     *
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: The space between the button's border and the label.
     */
    public void setMargin(Insets m) {
        // Cache the old margin if it comes from the UI
        if(m instanceof UIResource) {
            defaultMargin = m;
        } else if(margin instanceof UIResource) {
            defaultMargin = margin;
        }

        // If the client passes in a null insets, restore the margin
        // from the UI if possible
        if(m == null && defaultMargin != null) {
            m = defaultMargin;
        }

        Insets old = margin;
        margin = m;
        firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
        if (old == null || !old.equals(m)) {
            revalidate();
            repaint();
        }
    }

    /**
     * Returns the margin between the button's border and
     * the label.
     *
     * @return an <code>Insets object specifying the margin
     *          between the botton's border and the label
     * @see #setMargin
     */
    public Insets getMargin() {
        return (margin == null) ? null : (Insets) margin.clone();
    }

    /**
     * Returns the default icon.
     * @return the default <code>Icon
     * @see #setIcon
     */
    public Icon getIcon() {
        return defaultIcon;
    }

    /**
     * Sets the button's default icon. This icon is
     * also used as the "pressed" and "disabled" icon if
     * there is no explicitly set pressed icon.
     *
     * @param defaultIcon the icon used as the default image
     * @see #getIcon
     * @see #setPressedIcon
     * @beaninfo
     *           bound: true
     *       attribute: visualUpdate true
     *     description: The button's default icon
     */
    public void setIcon(Icon defaultIcon) {
        Icon oldValue = this.defaultIcon;
        this.defaultIcon = defaultIcon;

        /* If the default icon has really changed and we had
         * generated the disabled icon for this component,
         * (i.e. setDisabledIcon() was never called) then
         * clear the disabledIcon field.
         */
        if (defaultIcon != oldValue && (disabledIcon instanceof UIResource)) {
            disabledIcon = null;
        }

        firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, defaultIcon);
        }
        if (defaultIcon != oldValue) {
            if (defaultIcon == null || oldValue == null ||
                defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
                defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
                revalidate();
            }
            repaint();
        }
    }

    /**
     * Returns the pressed icon for the button.
     * @return the <code>pressedIcon property
     * @see #setPressedIcon
     */
    public Icon getPressedIcon() {
        return pressedIcon;
    }

    /**
     * Sets the pressed icon for the button.
     * @param pressedIcon the icon used as the "pressed" image
     * @see #getPressedIcon
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: The pressed icon for the button.
     */
    public void setPressedIcon(Icon pressedIcon) {
        Icon oldValue = this.pressedIcon;
        this.pressedIcon = pressedIcon;
        firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, pressedIcon);
        }
        if (pressedIcon != oldValue) {
            if (getModel().isPressed()) {
                repaint();
            }
        }
    }

    /**
     * Returns the selected icon for the button.
     * @return the <code>selectedIcon property
     * @see #setSelectedIcon
     */
    public Icon getSelectedIcon() {
        return selectedIcon;
    }

    /**
     * Sets the selected icon for the button.
     * @param selectedIcon the icon used as the "selected" image
     * @see #getSelectedIcon
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: The selected icon for the button.
     */
    public void setSelectedIcon(Icon selectedIcon) {
        Icon oldValue = this.selectedIcon;
        this.selectedIcon = selectedIcon;

        /* If the default selected icon has really changed and we had
         * generated the disabled selected icon for this component,
         * (i.e. setDisabledSelectedIcon() was never called) then
         * clear the disabledSelectedIcon field.
         */
        if (selectedIcon != oldValue &&
            disabledSelectedIcon instanceof UIResource) {

            disabledSelectedIcon = null;
        }

        firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, selectedIcon);
        }
        if (selectedIcon != oldValue) {
            if (isSelected()) {
                repaint();
            }
        }
    }

    /**
     * Returns the rollover icon for the button.
     * @return the <code>rolloverIcon property
     * @see #setRolloverIcon
     */
    public Icon getRolloverIcon() {
        return rolloverIcon;
    }

    /**
     * Sets the rollover icon for the button.
     * @param rolloverIcon the icon used as the "rollover" image
     * @see #getRolloverIcon
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: The rollover icon for the button.
     */
    public void setRolloverIcon(Icon rolloverIcon) {
        Icon oldValue = this.rolloverIcon;
        this.rolloverIcon = rolloverIcon;
        firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, rolloverIcon);
        }
        setRolloverEnabled(true);
        if (rolloverIcon != oldValue) {
            // No way to determine whether we are currently in
            // a rollover state, so repaint regardless
            repaint();
        }

    }

    /**
     * Returns the rollover selection icon for the button.
     * @return the <code>rolloverSelectedIcon property
     * @see #setRolloverSelectedIcon
     */
    public Icon getRolloverSelectedIcon() {
        return rolloverSelectedIcon;
    }

    /**
     * Sets the rollover selected icon for the button.
     * @param rolloverSelectedIcon the icon used as the
     *          "selected rollover" image
     * @see #getRolloverSelectedIcon
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: The rollover selected icon for the button.
     */
    public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
        Icon oldValue = this.rolloverSelectedIcon;
        this.rolloverSelectedIcon = rolloverSelectedIcon;
        firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, rolloverSelectedIcon);
        }
        setRolloverEnabled(true);
        if (rolloverSelectedIcon != oldValue) {
            // No way to determine whether we are currently in
            // a rollover state, so repaint regardless
            if (isSelected()) {
                repaint();
            }
        }
    }

    /**
     * Returns the icon used by the button when it's disabled.
     * If no disabled icon has been set this will forward the call to
     * the look and feel to construct an appropriate disabled Icon.
     * <p>
     * Some look and feels might not render the disabled Icon, in which
     * case they will ignore this.
     *
     * @return the <code>disabledIcon property
     * @see #getPressedIcon
     * @see #setDisabledIcon
     * @see javax.swing.LookAndFeel#getDisabledIcon
     */
    @Transient
    public Icon getDisabledIcon() {
        if (disabledIcon == null) {
            disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
            if (disabledIcon != null) {
                firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);
            }
        }
        return disabledIcon;
    }

    /**
     * Sets the disabled icon for the button.
     * @param disabledIcon the icon used as the disabled image
     * @see #getDisabledIcon
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: The disabled icon for the button.
     */
    public void setDisabledIcon(Icon disabledIcon) {
        Icon oldValue = this.disabledIcon;
        this.disabledIcon = disabledIcon;
        firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, disabledIcon);
        }
        if (disabledIcon != oldValue) {
            if (!isEnabled()) {
                repaint();
            }
        }
    }

    /**
     * Returns the icon used by the button when it's disabled and selected.
     * If no disabled selection icon has been set, this will forward
     * the call to the LookAndFeel to construct an appropriate disabled
     * Icon from the selection icon if it has been set and to
     * <code>getDisabledIcon() otherwise.
     * <p>
     * Some look and feels might not render the disabled selected Icon, in
     * which case they will ignore this.
     *
     * @return the <code>disabledSelectedIcon property
     * @see #getDisabledIcon
     * @see #setDisabledSelectedIcon
     * @see javax.swing.LookAndFeel#getDisabledSelectedIcon
     */
    public Icon getDisabledSelectedIcon() {
        if (disabledSelectedIcon == null) {
             if (selectedIcon != null) {
                 disabledSelectedIcon = UIManager.getLookAndFeel().
                         getDisabledSelectedIcon(this, getSelectedIcon());
             } else {
                 return getDisabledIcon();
             }
        }
        return disabledSelectedIcon;
    }

    /**
     * Sets the disabled selection icon for the button.
     * @param disabledSelectedIcon the icon used as the disabled
     *          selection image
     * @see #getDisabledSelectedIcon
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: The disabled selection icon for the button.
     */
    public void setDisabledSelectedIcon(Icon disabledSelectedIcon) {
        Icon oldValue = this.disabledSelectedIcon;
        this.disabledSelectedIcon = disabledSelectedIcon;
        firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, oldValue, disabledSelectedIcon);
        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                oldValue, disabledSelectedIcon);
        }
        if (disabledSelectedIcon != oldValue) {
            if (disabledSelectedIcon == null || oldValue == null ||
                disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
                disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
                revalidate();
            }
            if (!isEnabled() && isSelected()) {
                repaint();
            }
        }
    }

    /**
     * Returns the vertical alignment of the text and icon.
     *
     * @return the <code>verticalAlignment property, one of the
     *          following values:
     * <ul>
     * <li>{@code SwingConstants.CENTER} (the default)
     * <li>{@code SwingConstants.TOP}
     * <li>{@code SwingConstants.BOTTOM}
     * </ul>
     */
    public int getVerticalAlignment() {
        return verticalAlignment;
    }

    /**
     * Sets the vertical alignment of the icon and text.
     * @param alignment one of the following values:
     * <ul>
     * <li>{@code SwingConstants.CENTER} (the default)
     * <li>{@code SwingConstants.TOP}
     * <li>{@code SwingConstants.BOTTOM}
     * </ul>
     * @throws IllegalArgumentException if the alignment is not one of the legal
     *         values listed above
     * @beaninfo
     *        bound: true
     *         enum: TOP    SwingConstants.TOP
     *               CENTER SwingConstants.CENTER
     *               BOTTOM  SwingConstants.BOTTOM
     *    attribute: visualUpdate true
     *  description: The vertical alignment of the icon and text.
     */
    public void setVerticalAlignment(int alignment) {
        if (alignment == verticalAlignment) return;
        int oldValue = verticalAlignment;
        verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
        firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment);         repaint();
    }

    /**
     * Returns the horizontal alignment of the icon and text.
     * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
     * but subclasses such as {@code JCheckBox} may use a different default.
     *
     * @return the <code>horizontalAlignment property,
     *             one of the following values:
     * <ul>
     *   <li>{@code SwingConstants.RIGHT}
     *   <li>{@code SwingConstants.LEFT}
     *   <li>{@code SwingConstants.CENTER}
     *   <li>{@code SwingConstants.LEADING}
     *   <li>{@code SwingConstants.TRAILING}
     * </ul>
     */
    public int getHorizontalAlignment() {
        return horizontalAlignment;
    }

    /**
     * Sets the horizontal alignment of the icon and text.
     * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
     * but subclasses such as {@code JCheckBox} may use a different default.
     *
     * @param alignment the alignment value, one of the following values:
     * <ul>
     *   <li>{@code SwingConstants.RIGHT}
     *   <li>{@code SwingConstants.LEFT}
     *   <li>{@code SwingConstants.CENTER}
     *   <li>{@code SwingConstants.LEADING}
     *   <li>{@code SwingConstants.TRAILING}
     * </ul>
     * @throws IllegalArgumentException if the alignment is not one of the
     *         valid values
     * @beaninfo
     *        bound: true
     *         enum: LEFT     SwingConstants.LEFT
     *               CENTER   SwingConstants.CENTER
     *               RIGHT    SwingConstants.RIGHT
     *               LEADING  SwingConstants.LEADING
     *               TRAILING SwingConstants.TRAILING
     *    attribute: visualUpdate true
     *  description: The horizontal alignment of the icon and text.
     */
    public void setHorizontalAlignment(int alignment) {
        if (alignment == horizontalAlignment) return;
        int oldValue = horizontalAlignment;
        horizontalAlignment = checkHorizontalKey(alignment,
                                                 "horizontalAlignment");
        firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
                           oldValue, horizontalAlignment);
        repaint();
    }


    /**
     * Returns the vertical position of the text relative to the icon.
     * @return the <code>verticalTextPosition property,
     *          one of the following values:
     * <ul>
     * <li>{@code SwingConstants.CENTER} (the default)
     * <li>{@code SwingConstants.TOP}
     * <li>{@code SwingConstants.BOTTOM}
     * </ul>
     */
    public int getVerticalTextPosition() {
        return verticalTextPosition;
    }

    /**
     * Sets the vertical position of the text relative to the icon.
     * @param textPosition  one of the following values:
     * <ul>
     * <li>{@code SwingConstants.CENTER} (the default)
     * <li>{@code SwingConstants.TOP}
     * <li>{@code SwingConstants.BOTTOM}
     * </ul>
     * @beaninfo
     *        bound: true
     *         enum: TOP    SwingConstants.TOP
     *               CENTER SwingConstants.CENTER
     *               BOTTOM SwingConstants.BOTTOM
     *    attribute: visualUpdate true
     *  description: The vertical position of the text relative to the icon.
     */
    public void setVerticalTextPosition(int textPosition) {
        if (textPosition == verticalTextPosition) return;
        int oldValue = verticalTextPosition;
        verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
        firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
        revalidate();
        repaint();
    }

    /**
     * Returns the horizontal position of the text relative to the icon.
     * @return the <code>horizontalTextPosition property,
     *          one of the following values:
     * <ul>
     * <li>{@code SwingConstants.RIGHT}
     * <li>{@code SwingConstants.LEFT}
     * <li>{@code SwingConstants.CENTER}
     * <li>{@code SwingConstants.LEADING}
     * <li>{@code SwingConstants.TRAILING} (the default)
     * </ul>
     */
    public int getHorizontalTextPosition() {
        return horizontalTextPosition;
    }

    /**
     * Sets the horizontal position of the text relative to the icon.
     * @param textPosition one of the following values:
     * <ul>
     * <li>{@code SwingConstants.RIGHT}
     * <li>{@code SwingConstants.LEFT}
     * <li>{@code SwingConstants.CENTER}
     * <li>{@code SwingConstants.LEADING}
     * <li>{@code SwingConstants.TRAILING} (the default)
     * </ul>
     * @exception IllegalArgumentException if <code>textPosition
     *          is not one of the legal values listed above
     * @beaninfo
     *        bound: true
     *         enum: LEFT     SwingConstants.LEFT
     *               CENTER   SwingConstants.CENTER
     *               RIGHT    SwingConstants.RIGHT
     *               LEADING  SwingConstants.LEADING
     *               TRAILING SwingConstants.TRAILING
     *    attribute: visualUpdate true
     *  description: The horizontal position of the text relative to the icon.
     */
    public void setHorizontalTextPosition(int textPosition) {
        if (textPosition == horizontalTextPosition) return;
        int oldValue = horizontalTextPosition;
        horizontalTextPosition = checkHorizontalKey(textPosition,
                                                    "horizontalTextPosition");
        firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
                           oldValue,
                           horizontalTextPosition);
        revalidate();
        repaint();
    }

    /**
     * Returns the amount of space between the text and the icon
     * displayed in this button.
     *
     * @return an int equal to the number of pixels between the text
     *         and the icon.
     * @since 1.4
     * @see #setIconTextGap
     */
    public int getIconTextGap() {
        return iconTextGap;
    }

    /**
     * If both the icon and text properties are set, this property
     * defines the space between them.
     * <p>
     * The default value of this property is 4 pixels.
     * <p>
     * This is a JavaBeans bound property.
     *
     * @since 1.4
     * @see #getIconTextGap
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: If both the icon and text properties are set, this
     *               property defines the space between them.
     */
    public void setIconTextGap(int iconTextGap) {
        int oldValue = this.iconTextGap;
        this.iconTextGap = iconTextGap;
        iconTextGapSet = true;
        firePropertyChange("iconTextGap", oldValue, iconTextGap);
        if (iconTextGap != oldValue) {
            revalidate();
            repaint();
        }
    }

    /**
     * Verify that the {@code key} argument is a legal value for the
     * {@code horizontalAlignment} and {@code horizontalTextPosition}
     * properties. Valid values are:
     * <ul>
     *   <li>{@code SwingConstants.RIGHT}
     *   <li>{@code SwingConstants.LEFT}
     *   <li>{@code SwingConstants.CENTER}
     *   <li>{@code SwingConstants.LEADING}
     *   <li>{@code SwingConstants.TRAILING}
     * </ul>
     *
     * @param key the property value to check
     * @param exception the message to use in the
     *        {@code IllegalArgumentException} that is thrown for an invalid
     *        value
     * @return the {@code key} argument
     * @exception IllegalArgumentException if key is not one of the legal
     *            values listed above
     * @see #setHorizontalTextPosition
     * @see #setHorizontalAlignment
     */
    protected int checkHorizontalKey(int key, String exception) {
        if ((key == LEFT) ||
            (key == CENTER) ||
            (key == RIGHT) ||
            (key == LEADING) ||
            (key == TRAILING)) {
            return key;
        } else {
            throw new IllegalArgumentException(exception);
        }
    }

    /**
     * Verify that the {@code key} argument is a legal value for the
     * vertical properties. Valid values are:
     * <ul>
     *   <li>{@code SwingConstants.CENTER}
     *   <li>{@code SwingConstants.TOP}
     *   <li>{@code SwingConstants.BOTTOM}
     * </ul>
     *
     * @param key the property value to check
     * @param exception the message to use in the
     *        {@code IllegalArgumentException} that is thrown for an invalid
     *        value
     * @return the {@code key} argument
     * @exception IllegalArgumentException if key is not one of the legal
     *            values listed above
     */
    protected int checkVerticalKey(int key, String exception) {
        if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {
            return key;
        } else {
            throw new IllegalArgumentException(exception);
        }
    }

    /**
     *{@inheritDoc}
     *
     * @since 1.6
     */
    public void removeNotify() {
        super.removeNotify();
        if(isRolloverEnabled()) {
            getModel().setRollover(false);
        }
    }

    /**
     * Sets the action command for this button.
     * @param actionCommand the action command for this button
     */
    public void setActionCommand(String actionCommand) {
        getModel().setActionCommand(actionCommand);
    }

    /**
     * Returns the action command for this button.
     * @return the action command for this button
     */
    public String getActionCommand() {
        String ac = getModel().getActionCommand();
        if(ac == null) {
            ac = getText();
        }
        return ac;
    }

    private Action action;
    private PropertyChangeListener actionPropertyChangeListener;

    /**
     * Sets the <code>Action.
     * The new <code>Action replaces any previously set
     * <code>Action but does not affect ActionListeners
     * independently added with <code>addActionListener.
     * If the <code>Action is already a registered
     * <code>ActionListener for the button, it is not re-registered.
     * <p>
     * Setting the <code>Action results in immediately changing
     * all the properties described in <a href="Action.html#buttonActions">
     * Swing Components Supporting <code>Action.
     * Subsequently, the button's properties are automatically updated
     * as the <code>Action's properties change.
     * <p>
     * This method uses three other methods to set
     * and help track the <code>Action's property values.
     * It uses the <code>configurePropertiesFromAction method
     * to immediately change the button's properties.
     * To track changes in the <code>Action's property values,
     * this method registers the <code>PropertyChangeListener
     * returned by <code>createActionPropertyChangeListener. The
     * default {@code PropertyChangeListener} invokes the
     * {@code actionPropertyChanged} method when a property in the
     * {@code Action} changes.
     *
     * @param a the <code>Action for the AbstractButton,
     *          or <code>null
     * @since 1.3
     * @see Action
     * @see #getAction
     * @see #configurePropertiesFromAction
     * @see #createActionPropertyChangeListener
     * @see #actionPropertyChanged
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: the Action instance connected with this ActionEvent source
     */
    public void setAction(Action a) {
        Action oldValue = getAction();
        if (action==null || !action.equals(a)) {
            action = a;
            if (oldValue!=null) {
                removeActionListener(oldValue);
                oldValue.removePropertyChangeListener(actionPropertyChangeListener);
                actionPropertyChangeListener = null;
            }
            configurePropertiesFromAction(action);
            if (action!=null) {
                // Don't add if it is already a listener
                if (!isListener(ActionListener.class, action)) {
                    addActionListener(action);
                }
                // Reverse linkage:
                actionPropertyChangeListener = createActionPropertyChangeListener(action);
                action.addPropertyChangeListener(actionPropertyChangeListener);
            }
            firePropertyChange("action", oldValue, action);
        }
    }

    private boolean isListener(Class c, ActionListener a) {
        boolean isListener = false;
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==c && listeners[i+1]==a) {
                    isListener=true;
            }
        }
        return isListener;
    }

    /**
     * Returns the currently set <code>Action for this
     * <code>ActionEvent source, or null
     * if no <code>Action is set.
     *
     * @return the <code>Action for this ActionEvent
     *          source, or <code>null
     * @since 1.3
     * @see Action
     * @see #setAction
     */
    public Action getAction() {
        return action;
    }

    /**
     * Sets the properties on this button to match those in the specified
     * <code>Action.  Refer to 
     * Swing Components Supporting <code>Action for more
     * details as to which properties this sets.
     *
     * @param a the <code>Action from which to get the properties,
     *          or <code>null
     * @since 1.3
     * @see Action
     * @see #setAction
     */
    protected void configurePropertiesFromAction(Action a) {
        setMnemonicFromAction(a);
        setTextFromAction(a, false);
        AbstractAction.setToolTipTextFromAction(this, a);
        setIconFromAction(a);
        setActionCommandFromAction(a);
        AbstractAction.setEnabledFromAction(this, a);
        if (AbstractAction.hasSelectedKey(a) &&
                shouldUpdateSelectedStateFromAction()) {
            setSelectedFromAction(a);
        }
        setDisplayedMnemonicIndexFromAction(a, false);
    }

    void clientPropertyChanged(Object key, Object oldValue,
                               Object newValue) {
        if (key == "hideActionText") {
            boolean current = (newValue instanceof Boolean) ?
                                (Boolean)newValue : false;
            if (getHideActionText() != current) {
                setHideActionText(current);
            }
        }
    }

    /**
     * Button subclasses that support mirroring the selected state from
     * the action should override this to return true.  AbstractButton's
     * implementation returns false.
     */
    boolean shouldUpdateSelectedStateFromAction() {
        return false;
    }

    /**
     * Updates the button's state in response to property changes in the
     * associated action. This method is invoked from the
     * {@code PropertyChangeListener} returned from
     * {@code createActionPropertyChangeListener}. Subclasses do not normally
     * need to invoke this. Subclasses that support additional {@code Action}
     * properties should override this and
     * {@code configurePropertiesFromAction}.
     * <p>
     * Refer to the table at <a href="Action.html#buttonActions">
     * Swing Components Supporting <code>Action for a list of
     * the properties this method sets.
     *
     * @param action the <code>Action associated with this button
     * @param propertyName the name of the property that changed
     * @since 1.6
     * @see Action
     * @see #configurePropertiesFromAction
     */
    protected void actionPropertyChanged(Action action, String propertyName) {
        if (propertyName == Action.NAME) {
            setTextFromAction(action, true);
        } else if (propertyName == "enabled") {
            AbstractAction.setEnabledFromAction(this, action);
        } else if (propertyName == Action.SHORT_DESCRIPTION) {
            AbstractAction.setToolTipTextFromAction(this, action);
        } else if (propertyName == Action.SMALL_ICON) {
            smallIconChanged(action);
        } else if (propertyName == Action.MNEMONIC_KEY) {
            setMnemonicFromAction(action);
        } else if (propertyName == Action.ACTION_COMMAND_KEY) {
            setActionCommandFromAction(action);
        } else if (propertyName == Action.SELECTED_KEY &&
                   AbstractAction.hasSelectedKey(action) &&
                   shouldUpdateSelectedStateFromAction()) {
            setSelectedFromAction(action);
        } else if (propertyName == Action.DISPLAYED_MNEMONIC_INDEX_KEY) {
            setDisplayedMnemonicIndexFromAction(action, true);
        } else if (propertyName == Action.LARGE_ICON_KEY) {
            largeIconChanged(action);
        }
    }

    private void setDisplayedMnemonicIndexFromAction(
            Action a, boolean fromPropertyChange) {
        Integer iValue = (a == null) ? null :
                (Integer)a.getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY);
        if (fromPropertyChange || iValue != null) {
            int value;
            if (iValue == null) {
                value = -1;
            } else {
                value = iValue;
                String text = getText();
                if (text == null || value >= text.length()) {
                    value = -1;
                }
            }
            setDisplayedMnemonicIndex(value);
        }
    }

    private void setMnemonicFromAction(Action a) {
        Integer n = (a == null) ? null :
                                  (Integer)a.getValue(Action.MNEMONIC_KEY);
        setMnemonic((n == null) ? '\0' : n);
    }

    private void setTextFromAction(Action a, boolean propertyChange) {
        boolean hideText = getHideActionText();
        if (!propertyChange) {
            setText((a != null && !hideText) ?
                        (String)a.getValue(Action.NAME) : null);
        }
        else if (!hideText) {
            setText((String)a.getValue(Action.NAME));
        }
    }

    void setIconFromAction(Action a) {
        Icon icon = null;
        if (a != null) {
            icon = (Icon)a.getValue(Action.LARGE_ICON_KEY);
            if (icon == null) {
                icon = (Icon)a.getValue(Action.SMALL_ICON);
            }
        }
        setIcon(icon);
    }

    void smallIconChanged(Action a) {
        if (a.getValue(Action.LARGE_ICON_KEY) == null) {
            setIconFromAction(a);
        }
    }

    void largeIconChanged(Action a) {
        setIconFromAction(a);
    }

    private void setActionCommandFromAction(Action a) {
        setActionCommand((a != null) ?
                             (String)a.getValue(Action.ACTION_COMMAND_KEY) :
                             null);
    }

    /**
     * Sets the seleted state of the button from the action.  This is defined
     * here, but not wired up.  Subclasses like JToggleButton and
     * JCheckBoxMenuItem make use of it.
     *
     * @param a the Action
     */
    private void setSelectedFromAction(Action a) {
        boolean selected = false;
        if (a != null) {
            selected = AbstractAction.isSelected(a);
        }
        if (selected != isSelected()) {
            // This won't notify ActionListeners, but that should be
            // ok as the change is coming from the Action.
            setSelected(selected);
            // Make sure the change actually took effect
            if (!selected && isSelected()) {
                if (getModel() instanceof DefaultButtonModel) {
                    ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup();
                    if (group != null) {
                        group.clearSelection();
                    }
                }
            }
        }
    }

    /**
     * Creates and returns a <code>PropertyChangeListener that is
     * responsible for listening for changes from the specified
     * <code>Action and updating the appropriate properties.
     * <p>
     * <b>Warning: If you subclass this do not create an anonymous
     * inner class.  If you do the lifetime of the button will be tied to
     * that of the <code>Action.
     *
     * @param a the button's action
     * @since 1.3
     * @see Action
     * @see #setAction
     */
    protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
        return createActionPropertyChangeListener0(a);
    }


    PropertyChangeListener createActionPropertyChangeListener0(Action a) {
        return new ButtonActionPropertyChangeListener(this, a);
    }

    @SuppressWarnings("serial")
    private static class ButtonActionPropertyChangeListener
                 extends ActionPropertyChangeListener<AbstractButton> {
        ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
            super(b, a);
        }
        protected void actionPropertyChanged(AbstractButton button,
                                             Action action,
                                             PropertyChangeEvent e) {
            if (AbstractAction.shouldReconfigure(e)) {
                button.configurePropertiesFromAction(action);
            } else {
                button.actionPropertyChanged(action, e.getPropertyName());
            }
        }
    }

    /**
     * Gets the <code>borderPainted property.
     *
     * @return the value of the <code>borderPainted property
     * @see #setBorderPainted
     */
    public boolean isBorderPainted() {
        return paintBorder;
    }

    /**
     * Sets the <code>borderPainted property.
     * If <code>true and the button has a border,
     * the border is painted. The default value for the
     * <code>borderPainted property is true.
     * <p>
     * Some look and feels might not support
     * the <code>borderPainted property,
     * in which case they ignore this.
     *
     * @param b if true and border property is not <code>null,
     *          the border is painted
     * @see #isBorderPainted
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Whether the border should be painted.
     */
    public void setBorderPainted(boolean b) {
        boolean oldValue = paintBorder;
        paintBorder = b;
        borderPaintedSet = true;
        firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
        if (b != oldValue) {
            revalidate();
            repaint();
        }
    }

    /**
     * Paint the button's border if <code>BorderPainted
     * property is true and the button has a border.
     * @param g the <code>Graphics context in which to paint
     *
     * @see #paint
     * @see #setBorder
     */
    protected void paintBorder(Graphics g) {
        if (isBorderPainted()) {
            super.paintBorder(g);
        }
    }

    /**
     * Gets the <code>paintFocus property.
     *
     * @return the <code>paintFocus property
     * @see #setFocusPainted
     */
    public boolean isFocusPainted() {
        return paintFocus;
    }

    /**
     * Sets the <code>paintFocus property, which must
     * be <code>true for the focus state to be painted.
     * The default value for the <code>paintFocus property
     * is <code>true.
     * Some look and feels might not paint focus state;
     * they will ignore this property.
     *
     * @param b if <code>true, the focus state should be painted
     * @see #isFocusPainted
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Whether focus should be painted
     */
    public void setFocusPainted(boolean b) {
        boolean oldValue = paintFocus;
        paintFocus = b;
        firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
        if (b != oldValue && isFocusOwner()) {
            revalidate();
            repaint();
        }
    }

    /**
     * Gets the <code>contentAreaFilled property.
     *
     * @return the <code>contentAreaFilled property
     * @see #setContentAreaFilled
     */
    public boolean isContentAreaFilled() {
        return contentAreaFilled;
    }

    /**
     * Sets the <code>contentAreaFilled property.
     * If <code>true the button will paint the content
     * area.  If you wish to have a transparent button, such as
     * an icon only button, for example, then you should set
     * this to <code>false. Do not call setOpaque(false).
     * The default value for the the <code>contentAreaFilled
     * property is <code>true.
     * <p>
     * This function may cause the component's opaque property to change.
     * <p>
     * The exact behavior of calling this function varies on a
     * component-by-component and L&F-by-L&F basis.
     *
     * @param b if true, the content should be filled; if false
     *          the content area is not filled
     * @see #isContentAreaFilled
     * @see #setOpaque
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Whether the button should paint the content area
     *               or leave it transparent.
     */
    public void setContentAreaFilled(boolean b) {
        boolean oldValue = contentAreaFilled;
        contentAreaFilled = b;
        contentAreaFilledSet = true;
        firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
        if (b != oldValue) {
            repaint();
        }
    }

    /**
     * Gets the <code>rolloverEnabled property.
     *
     * @return the value of the <code>rolloverEnabled property
     * @see #setRolloverEnabled
     */
    public boolean isRolloverEnabled() {
        return rolloverEnabled;
    }

    /**
     * Sets the <code>rolloverEnabled property, which
     * must be <code>true for rollover effects to occur.
     * The default value for the <code>rolloverEnabled
     * property is <code>false.
     * Some look and feels might not implement rollover effects;
     * they will ignore this property.
     *
     * @param b if <code>true, rollover effects should be painted
     * @see #isRolloverEnabled
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Whether rollover effects should be enabled.
     */
    public void setRolloverEnabled(boolean b) {
        boolean oldValue = rolloverEnabled;
        rolloverEnabled = b;
        rolloverEnabledSet = true;
        firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
        if (b != oldValue) {
            repaint();
        }
    }

    /**
     * Returns the keyboard mnemonic from the the current model.
     * @return the keyboard mnemonic from the model
     */
    public int getMnemonic() {
        return mnemonic;
    }

    /**
     * Sets the keyboard mnemonic on the current model.
     * The mnemonic is the key which when combined with the look and feel's
     * mouseless modifier (usually Alt) will activate this button
     * if focus is contained somewhere within this button's ancestor
     * window.
     * <p>
     * A mnemonic must correspond to a single key on the keyboard
     * and should be specified using one of the <code>VK_XXX
     * keycodes defined in <code>java.awt.event.KeyEvent.
     * These codes and the wider array of codes for international
     * keyboards may be obtained through
     * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar.
     * Mnemonics are case-insensitive, therefore a key event
     * with the corresponding keycode would cause the button to be
     * activated whether or not the Shift modifier was pressed.
     * <p>
     * If the character defined by the mnemonic is found within
     * the button's label string, the first occurrence of it
     * will be underlined to indicate the mnemonic to the user.
     *
     * @param mnemonic the key code which represents the mnemonic
     * @see     java.awt.event.KeyEvent
     * @see     #setDisplayedMnemonicIndex
     *
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: the keyboard character mnemonic
     */
    public void setMnemonic(int mnemonic) {
        int oldValue = getMnemonic();
        model.setMnemonic(mnemonic);
        updateMnemonicProperties();
    }

    /**
     * This method is now obsolete, please use <code>setMnemonic(int)
     * to set the mnemonic for a button.  This method is only designed
     * to handle character values which fall between 'a' and 'z' or
     * 'A' and 'Z'.
     *
     * @param mnemonic  a char specifying the mnemonic value
     * @see #setMnemonic(int)
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: the keyboard character mnemonic
     */
    public void setMnemonic(char mnemonic) {
        int vk = (int) mnemonic;
        if(vk >= 'a' && vk <='z')
            vk -= ('a' - 'A');
        setMnemonic(vk);
    }

    /**
     * Provides a hint to the look and feel as to which character in the
     * text should be decorated to represent the mnemonic. Not all look and
     * feels may support this. A value of -1 indicates either there is no
     * mnemonic, the mnemonic character is not contained in the string, or
     * the developer does not wish the mnemonic to be displayed.
     * <p>
     * The value of this is updated as the properties relating to the
     * mnemonic change (such as the mnemonic itself, the text...).
     * You should only ever have to call this if
     * you do not wish the default character to be underlined. For example, if
     * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
     * to be decorated, as 'Save <u>As', you would have to invoke
     * <code>setDisplayedMnemonicIndex(5) after invoking
     * <code>setMnemonic(KeyEvent.VK_A).
     *
     * @since 1.4
     * @param index Index into the String to underline
     * @exception IllegalArgumentException will be thrown if <code>index
     *            is >= length of the text, or < -1
     * @see #getDisplayedMnemonicIndex
     *
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: the index into the String to draw the keyboard character
     *               mnemonic at
     */
    public void setDisplayedMnemonicIndex(int index)
                                          throws IllegalArgumentException {
        int oldValue = mnemonicIndex;
        if (index == -1) {
            mnemonicIndex = -1;
        } else {
            String text = getText();
            int textLength = (text == null) ? 0 : text.length();
            if (index < -1 || index >= textLength) {  // index out of range
                throw new IllegalArgumentException("index == " + index);
            }
        }
        mnemonicIndex = index;
        firePropertyChange("displayedMnemonicIndex", oldValue, index);
        if (index != oldValue) {
            revalidate();
            repaint();
        }
    }

    /**
     * Returns the character, as an index, that the look and feel should
     * provide decoration for as representing the mnemonic character.
     *
     * @since 1.4
     * @return index representing mnemonic character
     * @see #setDisplayedMnemonicIndex
     */
    public int getDisplayedMnemonicIndex() {
        return mnemonicIndex;
    }

    /**
     * Update the displayedMnemonicIndex property. This method
     * is called when either text or mnemonic changes. The new
     * value of the displayedMnemonicIndex property is the index
     * of the first occurrence of mnemonic in text.
     */
    private void updateDisplayedMnemonicIndex(String text, int mnemonic) {
        setDisplayedMnemonicIndex(
            SwingUtilities.findDisplayedMnemonicIndex(text, mnemonic));
    }

    /**
     * Brings the mnemonic property in accordance with model's mnemonic.
     * This is called when model's mnemonic changes. Also updates the
     * displayedMnemonicIndex property.
     */
    private void updateMnemonicProperties() {
        int newMnemonic = model.getMnemonic();
        if (mnemonic != newMnemonic) {
            int oldValue = mnemonic;
            mnemonic = newMnemonic;
            firePropertyChange(MNEMONIC_CHANGED_PROPERTY,
                               oldValue, mnemonic);
            updateDisplayedMnemonicIndex(getText(), mnemonic);
            revalidate();
            repaint();
        }
    }

    /**
     * Sets the amount of time (in milliseconds) required between
     * mouse press events for the button to generate the corresponding
     * action events.  After the initial mouse press occurs (and action
     * event generated) any subsequent mouse press events which occur
     * on intervals less than the threshhold will be ignored and no
     * corresponding action event generated.  By default the threshhold is 0,
     * which means that for each mouse press, an action event will be
     * fired, no matter how quickly the mouse clicks occur.  In buttons
     * where this behavior is not desirable (for example, the "OK" button
     * in a dialog), this threshhold should be set to an appropriate
     * positive value.
     *
     * @see #getMultiClickThreshhold
     * @param threshhold the amount of time required between mouse
     *        press events to generate corresponding action events
     * @exception   IllegalArgumentException if threshhold < 0
     * @since 1.4
     */
    public void setMultiClickThreshhold(long threshhold) {
        if (threshhold < 0) {
            throw new IllegalArgumentException("threshhold must be >= 0");
        }
        this.multiClickThreshhold = threshhold;
    }

    /**
     * Gets the amount of time (in milliseconds) required between
     * mouse press events for the button to generate the corresponding
     * action events.
     *
     * @see #setMultiClickThreshhold
     * @return the amount of time required between mouse press events
     *         to generate corresponding action events
     * @since 1.4
     */
    public long getMultiClickThreshhold() {
        return multiClickThreshhold;
    }

    /**
     * Returns the model that this button represents.
     * @return the <code>model property
     * @see #setModel
     */
    public ButtonModel getModel() {
        return model;
    }

    /**
     * Sets the model that this button represents.
     * @param newModel the new <code>ButtonModel
     * @see #getModel
     * @beaninfo
     *        bound: true
     *  description: Model that the Button uses.
     */
    public void setModel(ButtonModel newModel) {

        ButtonModel oldModel = getModel();

        if (oldModel != null) {
            oldModel.removeChangeListener(changeListener);
            oldModel.removeActionListener(actionListener);
            oldModel.removeItemListener(itemListener);
            changeListener = null;
            actionListener = null;
            itemListener = null;
        }

        model = newModel;

        if (newModel != null) {
            changeListener = createChangeListener();
            actionListener = createActionListener();
            itemListener = createItemListener();
            newModel.addChangeListener(changeListener);
            newModel.addActionListener(actionListener);
            newModel.addItemListener(itemListener);

            updateMnemonicProperties();
            //We invoke setEnabled() from JComponent
            //because setModel() can be called from a constructor
            //when the button is not fully initialized
            super.setEnabled(newModel.isEnabled());

        } else {
            mnemonic = '\0';
        }

        updateDisplayedMnemonicIndex(getText(), mnemonic);

        firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
        if (newModel != oldModel) {
            revalidate();
            repaint();
        }
    }


    /**
     * Returns the L&F object that renders this component.
     * @return the ButtonUI object
     * @see #setUI
     */
    public ButtonUI getUI() {
        return (ButtonUI) ui;
    }


    /**
     * Sets the L&F object that renders this component.
     * @param ui the <code>ButtonUI L&F object
     * @see #getUI
     * @beaninfo
     *        bound: true
     *       hidden: true
     *    attribute: visualUpdate true
     *  description: The UI object that implements the LookAndFeel.
     */
    public void setUI(ButtonUI ui) {
        super.setUI(ui);
        // disabled icons are generated by the LF so they should be unset here
        if (disabledIcon instanceof UIResource) {
            setDisabledIcon(null);
        }
        if (disabledSelectedIcon instanceof UIResource) {
            setDisabledSelectedIcon(null);
        }
    }


    /**
     * Resets the UI property to a value from the current look
     * and feel.  Subtypes of <code>AbstractButton
     * should override this to update the UI. For
     * example, <code>JButton might do the following:
     * <pre>
     *      setUI((ButtonUI)UIManager.getUI(
     *          "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
     * </pre>
     */
    public void updateUI() {
    }

    /**
     * Adds the specified component to this container at the specified
     * index, refer to
     * {@link java.awt.Container#addImpl(Component, Object, int)}
     * for a complete description of this method.
     *
     * @param     comp the component to be added
     * @param     constraints an object expressing layout constraints
     *                 for this component
     * @param     index the position in the container's list at which to
     *                 insert the component, where <code>-1
     *                 means append to the end
     * @exception IllegalArgumentException if <code>index is invalid
     * @exception IllegalArgumentException if adding the container's parent
     *                  to itself
     * @exception IllegalArgumentException if adding a window to a container
     * @since 1.5
     */
    protected void addImpl(Component comp, Object constraints, int index) {
        if (!setLayout) {
            setLayout(new OverlayLayout(this));
        }
        super.addImpl(comp, constraints, index);
    }

    /**
     * Sets the layout manager for this container, refer to
     * {@link java.awt.Container#setLayout(LayoutManager)}
     * for a complete description of this method.
     *
     * @param mgr the specified layout manager
     * @since 1.5
     */
    public void setLayout(LayoutManager mgr) {
        setLayout = true;
        super.setLayout(mgr);
    }

    /**
     * Adds a <code>ChangeListener to the button.
     * @param l the listener to be added
     */
    public void addChangeListener(ChangeListener l) {
        listenerList.add(ChangeListener.class, l);
    }

    /**
     * Removes a ChangeListener from the button.
     * @param l the listener to be removed
     */
    public void removeChangeListener(ChangeListener l) {
        listenerList.remove(ChangeListener.class, l);
    }

    /**
     * Returns an array of all the <code>ChangeListeners added
     * to this AbstractButton with addChangeListener().
     *
     * @return all of the <code>ChangeListeners added or an empty
     *         array if no listeners have been added
     * @since 1.4
     */
    public ChangeListener[] getChangeListeners() {
        return listenerList.getListeners(ChangeListener.class);
    }

    /**
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance
     * is lazily created.
     * @see EventListenerList
     */
    protected void fireStateChanged() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ChangeListener.class) {
                // Lazily create the event:
                if (changeEvent == null)
                    changeEvent = new ChangeEvent(this);
                ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
            }
        }
    }

    /**
     * Adds an <code>ActionListener to the button.
     * @param l the <code>ActionListener to be added
     */
    public void addActionListener(ActionListener l) {
        listenerList.add(ActionListener.class, l);
    }

    /**
     * Removes an <code>ActionListener from the button.
     * If the listener is the currently set <code>Action
     * for the button, then the <code>Action
     * is set to <code>null.
     *
     * @param l the listener to be removed
     */
    public void removeActionListener(ActionListener l) {
        if ((l != null) && (getAction() == l)) {
            setAction(null);
        } else {
            listenerList.remove(ActionListener.class, l);
        }
    }

    /**
     * Returns an array of all the <code>ActionListeners added
     * to this AbstractButton with addActionListener().
     *
     * @return all of the <code>ActionListeners added or an empty
     *         array if no listeners have been added
     * @since 1.4
     */
    public ActionListener[] getActionListeners() {
        return listenerList.getListeners(ActionListener.class);
    }

    /**
     * Subclasses that want to handle <code>ChangeEvents differently
     * can override this to return another <code>ChangeListener
     * implementation.
     *
     * @return the new <code>ChangeListener
     */
    protected ChangeListener createChangeListener() {
        return getHandler();
    }

    /**
     * Extends <code>ChangeListener to be serializable.
     * <p>
     * <strong>Warning:
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans™
     * has been added to the <code>java.beans package.
     * Please see {@link java.beans.XMLEncoder}.
     */
    @SuppressWarnings("serial")
    protected class ButtonChangeListener implements ChangeListener, Serializable {
        // NOTE: This class is NOT used, instead the functionality has
        // been moved to Handler.
        ButtonChangeListener() {
        }

        public void stateChanged(ChangeEvent e) {
            getHandler().stateChanged(e);
        }
    }


    /**
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance
     * is lazily created using the <code>event
     * parameter.
     *
     * @param event  the <code>ActionEvent object
     * @see EventListenerList
     */
    protected void fireActionPerformed(ActionEvent event) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        ActionEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ActionListener.class) {
                // Lazily create the event:
                if (e == null) {
                      String actionCommand = event.getActionCommand();
                      if(actionCommand == null) {
                         actionCommand = getActionCommand();
                      }
                      e = new ActionEvent(AbstractButton.this,
                                          ActionEvent.ACTION_PERFORMED,
                                          actionCommand,
                                          event.getWhen(),
                                          event.getModifiers());
                }
                ((ActionListener)listeners[i+1]).actionPerformed(e);
            }
        }
    }

    /**
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance
     * is lazily created using the <code>event parameter.
     *
     * @param event  the <code>ItemEvent object
     * @see EventListenerList
     */
    protected void fireItemStateChanged(ItemEvent event) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        ItemEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ItemListener.class) {
                // Lazily create the event:
                if (e == null) {
                    e = new ItemEvent(AbstractButton.this,
                                      ItemEvent.ITEM_STATE_CHANGED,
                                      AbstractButton.this,
                                      event.getStateChange());
                }
                ((ItemListener)listeners[i+1]).itemStateChanged(e);
            }
        }
        if (accessibleContext != null) {
            if (event.getStateChange() == ItemEvent.SELECTED) {
                accessibleContext.firePropertyChange(
                    AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
                    null, AccessibleState.SELECTED);
                accessibleContext.firePropertyChange(
                    AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
                    Integer.valueOf(0), Integer.valueOf(1));
            } else {
                accessibleContext.firePropertyChange(
                    AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
                    AccessibleState.SELECTED, null);
                accessibleContext.firePropertyChange(
                    AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
                    Integer.valueOf(1), Integer.valueOf(0));
            }
        }
    }


    protected ActionListener createActionListener() {
        return getHandler();
    }


    protected ItemListener createItemListener() {
        return getHandler();
    }


    /**
     * Enables (or disables) the button.
     * @param b  true to enable the button, otherwise false
     */
    public void setEnabled(boolean b) {
        if (!b && model.isRollover()) {
            model.setRollover(false);
        }
        super.setEnabled(b);
        model.setEnabled(b);
    }

    // *** Deprecated java.awt.Button APIs below *** //

    /**
     * Returns the label text.
     *
     * @return a <code>String containing the label
     * @deprecated - Replaced by <code>getText
     */
    @Deprecated
    public String getLabel() {
        return getText();
    }

    /**
     * Sets the label text.
     *
     * @param label  a <code>String containing the text
     * @deprecated - Replaced by <code>setText(text)
     * @beaninfo
     *        bound: true
     *  description: Replace by setText(text)
     */
    @Deprecated
    public void setLabel(String label) {
        setText(label);
    }

    /**
     * Adds an <code>ItemListener to the checkbox.
     * @param l  the <code>ItemListener to be added
     */
    public void addItemListener(ItemListener l) {
        listenerList.add(ItemListener.class, l);
    }

    /**
     * Removes an <code>ItemListener from the button.
     * @param l the <code>ItemListener to be removed
     */
    public void removeItemListener(ItemListener l) {
        listenerList.remove(ItemListener.class, l);
    }

    /**
     * Returns an array of all the <code>ItemListeners added
     * to this AbstractButton with addItemListener().
     *
     * @return all of the <code>ItemListeners added or an empty
     *         array if no listeners have been added
     * @since 1.4
     */
    public ItemListener[] getItemListeners() {
        return listenerList.getListeners(ItemListener.class);
    }

   /**
     * Returns an array (length 1) containing the label or
     * <code>null if the button is not selected.
     *
     * @return an array containing 1 Object: the text of the button,
     *         if the item is selected; otherwise <code>null
     */
    public Object[] getSelectedObjects() {
        if (isSelected() == false) {
            return null;
        }
        Object[] selectedObjects = new Object[1];
        selectedObjects[0] = getText();
        return selectedObjects;
    }

    protected void init(String text, Icon icon) {
        if(text != null) {
            setText(text);
        }

        if(icon != null) {
            setIcon(icon);
        }

        // Set the UI
        updateUI();

        setAlignmentX(LEFT_ALIGNMENT);
        setAlignmentY(CENTER_ALIGNMENT);
    }


    /**
     * This is overridden to return false if the current <code>Icon's
     * <code>Image is not equal to the
     * passed in <code>Image img.
     *
     * @param img  the <code>Image to be compared
     * @param infoflags flags used to repaint the button when the image
     *          is updated and which determine how much is to be painted
     * @param x  the x coordinate
     * @param y  the y coordinate
     * @param w  the width
     * @param h  the height
     * @see     java.awt.image.ImageObserver
     * @see     java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
     */
    public boolean imageUpdate(Image img, int infoflags,
                               int x, int y, int w, int h) {
        Icon iconDisplayed = getIcon();
        if (iconDisplayed == null) {
            return false;
        }

        if (!model.isEnabled()) {
            if (model.isSelected()) {
                iconDisplayed = getDisabledSelectedIcon();
            } else {
                iconDisplayed = getDisabledIcon();
            }
        } else if (model.isPressed() && model.isArmed()) {
            iconDisplayed = getPressedIcon();
        } else if (isRolloverEnabled() && model.isRollover()) {
            if (model.isSelected()) {
                iconDisplayed = getRolloverSelectedIcon();
            } else {
                iconDisplayed = getRolloverIcon();
            }
        } else if (model.isSelected()) {
            iconDisplayed = getSelectedIcon();
        }

        if (!SwingUtilities.doesIconReferenceImage(iconDisplayed, img)) {
            // We don't know about this image, disable the notification so
            // we don't keep repainting.
            return false;
        }
        return super.imageUpdate(img, infoflags, x, y, w, h);
    }

    void setUIProperty(String propertyName, Object value) {
        if (propertyName == "borderPainted") {
            if (!borderPaintedSet) {
                setBorderPainted(((Boolean)value).booleanValue());
                borderPaintedSet = false;
            }
        } else if (propertyName == "rolloverEnabled") {
            if (!rolloverEnabledSet) {
                setRolloverEnabled(((Boolean)value).booleanValue());
                rolloverEnabledSet = false;
            }
        } else if (propertyName == "iconTextGap") {
            if (!iconTextGapSet) {
                setIconTextGap(((Number)value).intValue());
                iconTextGapSet = false;
            }
        } else if (propertyName == "contentAreaFilled") {
            if (!contentAreaFilledSet) {
                setContentAreaFilled(((Boolean)value).booleanValue());
                contentAreaFilledSet = false;
            }
        } else {
            super.setUIProperty(propertyName, value);
        }
    }

    /**
     * Returns a string representation of this <code>AbstractButton.
     * This method
     * is intended to be used only for debugging purposes, and the
     * content and format of the returned string may vary between
     * implementations. The returned string may be empty but may not
     * be <code>null.
     * <P>
     * Overriding <code>paramString to provide information about the
     * specific new aspects of the JFC components.
     *
     * @return  a string representation of this <code>AbstractButton
     */
    protected String paramString() {
        String defaultIconString = ((defaultIcon != null)
                                    && (defaultIcon != this) ?
                                    defaultIcon.toString() : "");
        String pressedIconString = ((pressedIcon != null)
                                    && (pressedIcon != this) ?
                                    pressedIcon.toString() : "");
        String disabledIconString = ((disabledIcon != null)
                                     && (disabledIcon != this) ?
                                     disabledIcon.toString() : "");
        String selectedIconString = ((selectedIcon != null)
                                     && (selectedIcon != this) ?
                                     selectedIcon.toString() : "");
        String disabledSelectedIconString = ((disabledSelectedIcon != null) &&
                                             (disabledSelectedIcon != this) ?
                                             disabledSelectedIcon.toString()
                                             : "");
        String rolloverIconString = ((rolloverIcon != null)
                                     && (rolloverIcon != this) ?
                                     rolloverIcon.toString() : "");
        String rolloverSelectedIconString = ((rolloverSelectedIcon != null) &&
                                             (rolloverSelectedIcon != this) ?
                                             rolloverSelectedIcon.toString()
                                             : "");
        String paintBorderString = (paintBorder ? "true" : "false");
        String paintFocusString = (paintFocus ? "true" : "false");
        String rolloverEnabledString = (rolloverEnabled ? "true" : "false");

        return super.paramString() +
        ",defaultIcon=" + defaultIconString +
        ",disabledIcon=" + disabledIconString +
        ",disabledSelectedIcon=" + disabledSelectedIconString +
        ",margin=" + margin +
        ",paintBorder=" + paintBorderString +
        ",paintFocus=" + paintFocusString +
        ",pressedIcon=" + pressedIconString +
        ",rolloverEnabled=" + rolloverEnabledString +
        ",rolloverIcon=" + rolloverIconString +
        ",rolloverSelectedIcon=" + rolloverSelectedIconString +
        ",selectedIcon=" + selectedIconString +
        ",text=" + text;
    }


    private Handler getHandler() {
        if (handler == null) {
            handler = new Handler();
        }
        return handler;
    }


    //
    // Listeners that are added to model
    //
    @SuppressWarnings("serial")
    class Handler implements ActionListener, ChangeListener, ItemListener,
                             Serializable {
        //
        // ChangeListener
        //
        public void stateChanged(ChangeEvent e) {
            Object source = e.getSource();

            updateMnemonicProperties();
            if (isEnabled() != model.isEnabled()) {
                setEnabled(model.isEnabled());
            }
            fireStateChanged();
            repaint();
        }

        //
        // ActionListener
        //
        public void actionPerformed(ActionEvent event) {
            fireActionPerformed(event);
        }

        //
        // ItemListener
        //
        public void itemStateChanged(ItemEvent event) {
            fireItemStateChanged(event);
            if (shouldUpdateSelectedStateFromAction()) {
                Action action = getAction();
                if (action != null && AbstractAction.hasSelectedKey(action)) {
                    boolean selected = isSelected();
                    boolean isActionSelected = AbstractAction.isSelected(
                              action);
                    if (isActionSelected != selected) {
                        action.putValue(Action.SELECTED_KEY, selected);
                    }
                }
            }
        }
    }

///////////////////
// Accessibility support
///////////////////
    /**
     * This class implements accessibility support for the
     * <code>AbstractButton class.  It provides an implementation of the
     * Java Accessibility API appropriate to button and menu item
     * user-interface elements.
     * <p>
     * <strong>Warning:
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans™
     * has been added to the <code>java.beans package.
     * Please see {@link java.beans.XMLEncoder}.
     * @since 1.4
     */
    protected abstract class AccessibleAbstractButton
        extends AccessibleJComponent implements AccessibleAction,
        AccessibleValue, AccessibleText, AccessibleExtendedComponent {

        /**
         * Returns the accessible name of this object.
         *
         * @return the localized name of the object -- can be
         *              <code>null if this
         *              object does not have a name
         */
        public String getAccessibleName() {
            String name = accessibleName;

            if (name == null) {
                name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
            }
            if (name == null) {
                name = AbstractButton.this.getText();
            }
            if (name == null) {
                name = super.getAccessibleName();
            }
            return name;
        }

        /**
         * Get the AccessibleIcons associated with this object if one
         * or more exist.  Otherwise return null.
         * @since 1.3
         */
        public AccessibleIcon [] getAccessibleIcon() {
            Icon defaultIcon = getIcon();

            if (defaultIcon instanceof Accessible) {
                AccessibleContext ac =
                    ((Accessible)defaultIcon).getAccessibleContext();
                if (ac != null && ac instanceof AccessibleIcon) {
                    return new AccessibleIcon[] { (AccessibleIcon)ac };
                }
            }
            return null;
        }

        /**
         * Get the state set of this object.
         *
         * @return an instance of AccessibleState containing the current state
         * of the object
         * @see AccessibleState
         */
        public AccessibleStateSet getAccessibleStateSet() {
        AccessibleStateSet states = super.getAccessibleStateSet();
            if (getModel().isArmed()) {
                states.add(AccessibleState.ARMED);
            }
            if (isFocusOwner()) {
                states.add(AccessibleState.FOCUSED);
            }
            if (getModel().isPressed()) {
                states.add(AccessibleState.PRESSED);
            }
            if (isSelected()) {
                states.add(AccessibleState.CHECKED);
            }
            return states;
        }

        /**
         * Get the AccessibleRelationSet associated with this object if one
         * exists.  Otherwise return null.
         * @see AccessibleRelation
         * @since 1.3
         */
        public AccessibleRelationSet getAccessibleRelationSet() {

            // Check where the AccessibleContext's relation
            // set already contains a MEMBER_OF relation.
            AccessibleRelationSet relationSet
                = super.getAccessibleRelationSet();

            if (!relationSet.contains(AccessibleRelation.MEMBER_OF)) {
                // get the members of the button group if one exists
                ButtonModel model = getModel();
                if (model != null && model instanceof DefaultButtonModel) {
                    ButtonGroup group = ((DefaultButtonModel)model).getGroup();
                    if (group != null) {
                        // set the target of the MEMBER_OF relation to be
                        // the members of the button group.
                        int len = group.getButtonCount();
                        Object [] target = new Object[len];
                        Enumeration<AbstractButton> elem = group.getElements();
                        for (int i = 0; i < len; i++) {
                            if (elem.hasMoreElements()) {
                                target[i] = elem.nextElement();
                            }
                        }
                        AccessibleRelation relation =
                            new AccessibleRelation(AccessibleRelation.MEMBER_OF);
                        relation.setTarget(target);
                        relationSet.add(relation);
                    }
                }
            }
            return relationSet;
        }

        /**
         * Get the AccessibleAction associated with this object.  In the
         * implementation of the Java Accessibility API for this class,
         * return this object, which is responsible for implementing the
         * AccessibleAction interface on behalf of itself.
         *
         * @return this object
         */
        public AccessibleAction getAccessibleAction() {
            return this;
        }

        /**
         * Get the AccessibleValue associated with this object.  In the
         * implementation of the Java Accessibility API for this class,
         * return this object, which is responsible for implementing the
         * AccessibleValue interface on behalf of itself.
         *
         * @return this object
         */
        public AccessibleValue getAccessibleValue() {
            return this;
        }

        /**
         * Returns the number of Actions available in this object.  The
         * default behavior of a button is to have one action - toggle
         * the button.
         *
         * @return 1, the number of Actions in this object
         */
        public int getAccessibleActionCount() {
            return 1;
        }

        /**
         * Return a description of the specified action of the object.
         *
         * @param i zero-based index of the actions
         */
        public String getAccessibleActionDescription(int i) {
            if (i == 0) {
                return UIManager.getString("AbstractButton.clickText");
            } else {
                return null;
            }
        }

        /**
         * Perform the specified Action on the object
         *
         * @param i zero-based index of actions
         * @return true if the the action was performed; else false.
         */
        public boolean doAccessibleAction(int i) {
            if (i == 0) {
                doClick();
                return true;
            } else {
                return false;
            }
        }

        /**
         * Get the value of this object as a Number.
         *
         * @return An Integer of 0 if this isn't selected or an Integer of 1 if
         * this is selected.
         * @see AbstractButton#isSelected
         */
        public Number getCurrentAccessibleValue() {
            if (isSelected()) {
                return Integer.valueOf(1);
            } else {
                return Integer.valueOf(0);
            }
        }

        /**
         * Set the value of this object as a Number.
         *
         * @return True if the value was set.
         */
        public boolean setCurrentAccessibleValue(Number n) {
            // TIGER - 4422535
            if (n == null) {
                return false;
            }
            int i = n.intValue();
            if (i == 0) {
                setSelected(false);
            } else {
                setSelected(true);
            }
            return true;
        }

        /**
         * Get the minimum value of this object as a Number.
         *
         * @return an Integer of 0.
         */
        public Number getMinimumAccessibleValue() {
            return Integer.valueOf(0);
        }

        /**
         * Get the maximum value of this object as a Number.
         *
         * @return An Integer of 1.
         */
        public Number getMaximumAccessibleValue() {
            return Integer.valueOf(1);
        }


        /* AccessibleText ---------- */

        public AccessibleText getAccessibleText() {
            View view = (View)AbstractButton.this.getClientProperty("html");
            if (view != null) {
                return this;
            } else {
                return null;
            }
        }

        /**
         * Given a point in local coordinates, return the zero-based index
         * of the character under that Point.  If the point is invalid,
         * this method returns -1.
         *
         * Note: the AbstractButton must have a valid size (e.g. have
         * been added to a parent container whose ancestor container
         * is a valid top-level window) for this method to be able
         * to return a meaningful value.
         *
         * @param p the Point in local coordinates
         * @return the zero-based index of the character under Point p; if
         * Point is invalid returns -1.
         * @since 1.3
         */
        public int getIndexAtPoint(Point p) {
            View view = (View) AbstractButton.this.getClientProperty("html");
            if (view != null) {
                Rectangle r = getTextRectangle();
                if (r == null) {
                    return -1;
                }
                Rectangle2D.Float shape =
                    new Rectangle2D.Float(r.x, r.y, r.width, r.height);
                Position.Bias bias[] = new Position.Bias[1];
                return view.viewToModel(p.x, p.y, shape, bias);
            } else {
                return -1;
            }
        }

        /**
         * Determine the bounding box of the character at the given
         * index into the string.  The bounds are returned in local
         * coordinates.  If the index is invalid an empty rectangle is
         * returned.
         *
         * Note: the AbstractButton must have a valid size (e.g. have
         * been added to a parent container whose ancestor container
         * is a valid top-level window) for this method to be able
         * to return a meaningful value.
         *
         * @param i the index into the String
         * @return the screen coordinates of the character's the bounding box,
         * if index is invalid returns an empty rectangle.
         * @since 1.3
         */
        public Rectangle getCharacterBounds(int i) {
            View view = (View) AbstractButton.this.getClientProperty("html");
            if (view != null) {
                Rectangle r = getTextRectangle();
                if (r == null) {
                    return null;
                }
                Rectangle2D.Float shape =
                    new Rectangle2D.Float(r.x, r.y, r.width, r.height);
                try {
                    Shape charShape =
                        view.modelToView(i, shape, Position.Bias.Forward);
                    return charShape.getBounds();
                } catch (BadLocationException e) {
                    return null;
                }
            } else {
                return null;
            }
        }

        /**
         * Return the number of characters (valid indicies)
         *
         * @return the number of characters
         * @since 1.3
         */
        public int getCharCount() {
            View view = (View) AbstractButton.this.getClientProperty("html");
            if (view != null) {
                Document d = view.getDocument();
                if (d instanceof StyledDocument) {
                    StyledDocument doc = (StyledDocument)d;
                    return doc.getLength();
                }
            }
            return accessibleContext.getAccessibleName().length();
        }

        /**
         * Return the zero-based offset of the caret.
         *
         * Note: That to the right of the caret will have the same index
         * value as the offset (the caret is between two characters).
         * @return the zero-based offset of the caret.
         * @since 1.3
         */
        public int getCaretPosition() {
            // There is no caret.
            return -1;
        }

        /**
         * Returns the String at a given index.
         *
         * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
         * or AccessibleText.SENTENCE to retrieve
         * @param index an index within the text >= 0
         * @return the letter, word, or sentence,
         *   null for an invalid index or part
         * @since 1.3
         */
        public String getAtIndex(int part, int index) {
            if (index < 0 || index >= getCharCount()) {
                return null;
            }
            switch (part) {
            case AccessibleText.CHARACTER:
                try {
                    return getText(index, 1);
                } catch (BadLocationException e) {
                    return null;
                }
            case AccessibleText.WORD:
                try {
                    String s = getText(0, getCharCount());
                    BreakIterator words = BreakIterator.getWordInstance(getLocale());
                    words.setText(s);
                    int end = words.following(index);
                    return s.substring(words.previous(), end);
                } catch (BadLocationException e) {
                    return null;
                }
            case AccessibleText.SENTENCE:
                try {
                    String s = getText(0, getCharCount());
                    BreakIterator sentence =
                        BreakIterator.getSentenceInstance(getLocale());
                    sentence.setText(s);
                    int end = sentence.following(index);
                    return s.substring(sentence.previous(), end);
                } catch (BadLocationException e) {
                    return null;
                }
            default:
                return null;
            }
        }

        /**
         * Returns the String after a given index.
         *
         * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
         * or AccessibleText.SENTENCE to retrieve
         * @param index an index within the text >= 0
         * @return the letter, word, or sentence, null for an invalid
         *  index or part
         * @since 1.3
         */
        public String getAfterIndex(int part, int index) {
            if (index < 0 || index >= getCharCount()) {
                return null;
            }
            switch (part) {
            case AccessibleText.CHARACTER:
                if (index+1 >= getCharCount()) {
                   return null;
                }
                try {
                    return getText(index+1, 1);
                } catch (BadLocationException e) {
                    return null;
                }
            case AccessibleText.WORD:
                try {
                    String s = getText(0, getCharCount());
                    BreakIterator words = BreakIterator.getWordInstance(getLocale());
                    words.setText(s);
                    int start = words.following(index);
                    if (start == BreakIterator.DONE || start >= s.length()) {
                        return null;
                    }
                    int end = words.following(start);
                    if (end == BreakIterator.DONE || end >= s.length()) {
                        return null;
                    }
                    return s.substring(start, end);
                } catch (BadLocationException e) {
                    return null;
                }
            case AccessibleText.SENTENCE:
                try {
                    String s = getText(0, getCharCount());
                    BreakIterator sentence =
                        BreakIterator.getSentenceInstance(getLocale());
                    sentence.setText(s);
                    int start = sentence.following(index);
                    if (start == BreakIterator.DONE || start > s.length()) {
                        return null;
                    }
                    int end = sentence.following(start);
                    if (end == BreakIterator.DONE || end > s.length()) {
                        return null;
                    }
                    return s.substring(start, end);
                } catch (BadLocationException e) {
                    return null;
                }
            default:
                return null;
            }
        }

        /**
         * Returns the String before a given index.
         *
         * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
         *   or AccessibleText.SENTENCE to retrieve
         * @param index an index within the text >= 0
         * @return the letter, word, or sentence, null for an invalid index
         *  or part
         * @since 1.3
         */
        public String getBeforeIndex(int part, int index) {
            if (index < 0 || index > getCharCount()-1) {
                return null;
            }
            switch (part) {
            case AccessibleText.CHARACTER:
                if (index == 0) {
                    return null;
                }
                try {
                    return getText(index-1, 1);
                } catch (BadLocationException e) {
                    return null;
                }
            case AccessibleText.WORD:
                try {
                    String s = getText(0, getCharCount());
                    BreakIterator words = BreakIterator.getWordInstance(getLocale());
                    words.setText(s);
                    int end = words.following(index);
                    end = words.previous();
                    int start = words.previous();
                    if (start == BreakIterator.DONE) {
                        return null;
                    }
                    return s.substring(start, end);
                } catch (BadLocationException e) {
                    return null;
                }
            case AccessibleText.SENTENCE:
                try {
                    String s = getText(0, getCharCount());
                    BreakIterator sentence =
                        BreakIterator.getSentenceInstance(getLocale());
                    sentence.setText(s);
                    int end = sentence.following(index);
                    end = sentence.previous();
                    int start = sentence.previous();
                    if (start == BreakIterator.DONE) {
                        return null;
                    }
                    return s.substring(start, end);
                } catch (BadLocationException e) {
                    return null;
                }
            default:
                return null;
            }
        }

        /**
         * Return the AttributeSet for a given character at a given index
         *
         * @param i the zero-based index into the text
         * @return the AttributeSet of the character
         * @since 1.3
         */
        public AttributeSet getCharacterAttribute(int i) {
            View view = (View) AbstractButton.this.getClientProperty("html");
            if (view != null) {
                Document d = view.getDocument();
                if (d instanceof StyledDocument) {
                    StyledDocument doc = (StyledDocument)d;
                    Element elem = doc.getCharacterElement(i);
                    if (elem != null) {
                        return elem.getAttributes();
                    }
                }
            }
            return null;
        }

        /**
         * Returns the start offset within the selected text.
         * If there is no selection, but there is
         * a caret, the start and end offsets will be the same.
         *
         * @return the index into the text of the start of the selection
         * @since 1.3
         */
        public int getSelectionStart() {
            // Text cannot be selected.
            return -1;
        }

        /**
         * Returns the end offset within the selected text.
         * If there is no selection, but there is
         * a caret, the start and end offsets will be the same.
         *
         * @return the index into the text of the end of the selection
         * @since 1.3
         */
        public int getSelectionEnd() {
            // Text cannot be selected.
            return -1;
        }

        /**
         * Returns the portion of the text that is selected.
         *
         * @return the String portion of the text that is selected
         * @since 1.3
         */
        public String getSelectedText() {
            // Text cannot be selected.
            return null;
        }

        /*
         * Returns the text substring starting at the specified
         * offset with the specified length.
         */
        private String getText(int offset, int length)
            throws BadLocationException {

            View view = (View) AbstractButton.this.getClientProperty("html");
            if (view != null) {
                Document d = view.getDocument();
                if (d instanceof StyledDocument) {
                    StyledDocument doc = (StyledDocument)d;
                    return doc.getText(offset, length);
                }
            }
            return null;
        }

        /*
         * Returns the bounding rectangle for the component text.
         */
        private Rectangle getTextRectangle() {

            String text = AbstractButton.this.getText();
            Icon icon = (AbstractButton.this.isEnabled()) ? AbstractButton.this.getIcon() : AbstractButton.this.getDisabledIcon();

            if ((icon == null) && (text == null)) {
                return null;
            }

            Rectangle paintIconR = new Rectangle();
            Rectangle paintTextR = new Rectangle();
            Rectangle paintViewR = new Rectangle();
            Insets paintViewInsets = new Insets(0, 0, 0, 0);

            paintViewInsets = AbstractButton.this.getInsets(paintViewInsets);
            paintViewR.x = paintViewInsets.left;
            paintViewR.y = paintViewInsets.top;
            paintViewR.width = AbstractButton.this.getWidth() - (paintViewInsets.left + paintViewInsets.right);
            paintViewR.height = AbstractButton.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);

            String clippedText = SwingUtilities.layoutCompoundLabel(
                AbstractButton.this,
                getFontMetrics(getFont()),
                text,
                icon,
                AbstractButton.this.getVerticalAlignment(),
                AbstractButton.this.getHorizontalAlignment(),
                AbstractButton.this.getVerticalTextPosition(),
                AbstractButton.this.getHorizontalTextPosition(),
                paintViewR,
                paintIconR,
                paintTextR,
                0);

            return paintTextR;
        }

        // ----- AccessibleExtendedComponent

        /**
         * Returns the AccessibleExtendedComponent
         *
         * @return the AccessibleExtendedComponent
         */
        AccessibleExtendedComponent getAccessibleExtendedComponent() {
            return this;
        }

        /**
         * Returns the tool tip text
         *
         * @return the tool tip text, if supported, of the object;
         * otherwise, null
         * @since 1.4
         */
        public String getToolTipText() {
            return AbstractButton.this.getToolTipText();
        }

        /**
         * Returns the titled border text
         *
         * @return the titled border text, if supported, of the object;
         * otherwise, null
         * @since 1.4
         */
        public String getTitledBorderText() {
            return super.getTitledBorderText();
        }

        /**
         * Returns key bindings associated with this object
         *
         * @return the key bindings, if supported, of the object;
         * otherwise, null
         * @see AccessibleKeyBinding
         * @since 1.4
         */
        public AccessibleKeyBinding getAccessibleKeyBinding() {
            int mnemonic = AbstractButton.this.getMnemonic();
            if (mnemonic == 0) {
                return null;
            }
            return new ButtonKeyBinding(mnemonic);
        }

        class ButtonKeyBinding implements AccessibleKeyBinding {
            int mnemonic;

            ButtonKeyBinding(int mnemonic) {
                this.mnemonic = mnemonic;
            }

            /**
             * Returns the number of key bindings for this object
             *
             * @return the zero-based number of key bindings for this object
             */
            public int getAccessibleKeyBindingCount() {
                return 1;
            }

            /**
             * Returns a key binding for this object.  The value returned is an
             * java.lang.Object which must be cast to appropriate type depending
             * on the underlying implementation of the key.  For example, if the
             * Object returned is a javax.swing.KeyStroke, the user of this
             * method should do the following:
             * <nf>
             * Component c = <get the component that has the key bindings>
             * AccessibleContext ac = c.getAccessibleContext();
             * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
             * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
             *     Object o = akb.getAccessibleKeyBinding(i);
             *     if (o instanceof javax.swing.KeyStroke) {
             *         javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
             *         <do something with the key binding>
             *     }
             * }
             * </code>
             *
             * @param i zero-based index of the key bindings
             * @return a javax.lang.Object which specifies the key binding
             * @exception IllegalArgumentException if the index is
             * out of bounds
             * @see #getAccessibleKeyBindingCount
             */
            public java.lang.Object getAccessibleKeyBinding(int i) {
                if (i != 0) {
                    throw new IllegalArgumentException();
                }
                return KeyStroke.getKeyStroke(mnemonic, 0);
            }
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java AbstractButton.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.