- /*
- * @(#)AbstractButton.java 1.174 04/05/18
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- package javax.swing;
- import java.awt.*;
- import java.awt.event.*;
- import java.awt.image.*;
- import java.text.*;
- import java.awt.geom.*;
- import java.beans.*;
- 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.
- * For further information see
- * <a
- href="http://java.sun.com/docs/books/tutorial/uiswing/components/button.html">How to Use Buttons, Check Boxes, and Radio Buttons</a>,
- * a section in <em>The Java Tutorial</em>.
- *
- * <p>
- *
- * <strong>Warning:</strong>
- * 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<sup><font size="-2">TM</font></sup>
- * has been added to the <code>java.beans</code> package.
- * Please see {@link java.beans.XMLEncoder}.
- *
- * @version 1.174 05/18/04
- * @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</code>.
- */
- protected ChangeListener changeListener = null;
- /**
- * The button model's <code>ActionListener</code>.
- */
- protected ActionListener actionListener = null;
- /**
- * The button model's <code>ItemListener</code>.
- */
- protected ItemListener itemListener = null;
- /**
- * Only one <code>ChangeEvent</code> 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;
- /**
- * 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</code>.
- * Call <code>doClick</code> to perform a programatic 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</code>
- * 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</code> will cause the button to
- * use the default margin. The button's default <code>Border</code>
- * 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</code> 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</code> 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</code>
- * @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</code> 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</code> 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</code> 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</code> 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</code> property
- * @see #getPressedIcon
- * @see #setDisabledIcon
- * @see javax.swing.LookAndFeel#getDisabledIcon
- */
- 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()</code> otherwise.
- * <p>
- * Some look and feels might not render the disabled selected Icon, in
- * which case they will ignore this.
- *
- * @return the <code>disabledSelectedIcon</code> 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</code> property, one of the
- * following values:
- * <ul>
- * <li>SwingConstants.CENTER (the default)
- * <li>SwingConstants.TOP
- * <li>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>SwingConstants.CENTER (the default)
- * <li>SwingConstants.TOP
- * <li>SwingConstants.BOTTOM
- * </ul>
- * @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.
- * @return the <code>horizontalAlignment</code> property,
- * one of the following values:
- * <ul>
- * <li>SwingConstants.RIGHT (the default)
- * <li>SwingConstants.LEFT
- * <li>SwingConstants.CENTER
- * <li>SwingConstants.LEADING
- * <li>SwingConstants.TRAILING
- * </ul>
- */
- public int getHorizontalAlignment() {
- return horizontalAlignment;
- }
- /**
- * Sets the horizontal alignment of the icon and text.
- * @param alignment one of the following values:
- * <ul>
- * <li>SwingConstants.RIGHT (the default)
- * <li>SwingConstants.LEFT
- * <li>SwingConstants.CENTER
- * <li>SwingConstants.LEADING
- * <li>SwingConstants.TRAILING
- * </ul>
- * @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</code> property,
- * one of the following values:
- * <ul>
- * <li>SwingConstants.CENTER (the default)
- * <li>SwingConstants.TOP
- * <li>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>SwingConstants.CENTER (the default)
- * <li>SwingConstants.TOP
- * <li>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);
- repaint();
- }
- /**
- * Returns the horizontal position of the text relative to the icon.
- * @return the <code>horizontalTextPosition</code> property,
- * one of the following values:
- * <ul>
- * <li>SwingConstants.RIGHT
- * <li>SwingConstants.LEFT
- * <li>SwingConstants.CENTER
- * <li>SwingConstants.LEADING
- * <li>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>SwingConstants.RIGHT
- * <li>SwingConstants.LEFT
- * <li>SwingConstants.CENTER
- * <li>SwingConstants.LEADING
- * <li>SwingConstants.TRAILING (the default)
- * </ul>
- * @exception IllegalArgumentException if <code>textPosition</code>
- * 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);
- 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 key is a legal value for the
- * <code>horizontalAlignment</code> properties.
- *
- * @param key the property value to check, one of the following values:
- * <ul>
- * <li>SwingConstants.RIGHT (the default)
- * <li>SwingConstants.LEFT
- * <li>SwingConstants.CENTER
- * <li>SwingConstants.LEADING
- * <li>SwingConstants.TRAILING
- * </ul>
- * @param exception the <code>IllegalArgumentException</code>
- * detail message
- * @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);
- }
- }
- /**
- * Ensures that the key is a valid. Throws an
- * <code>IllegalArgumentException</code>
- * exception otherwise.
- *
- * @param key the value to check, one of the following values:
- * <ul>
- * <li>SwingConstants.CENTER (the default)
- * <li>SwingConstants.TOP
- * <li>SwingConstants.BOTTOM
- * </ul>
- * @param exception a string to be passed to the
- * <code>IllegalArgumentException</code> call if key
- * is not one of the valid values listed above
- * @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);
- }
- }
- /**
- * 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</code> for the <code>ActionEvent</code> source.
- * The new <code>Action</code> replaces any previously set
- * <code>Action</code> but does not affect <code>ActionListeners</code>
- * independently added with <code>addActionListener</code>.
- * If the <code>Action</code> is already a registered
- * <code>ActionListener</code> for the button, it is not re-registered.
- * <p>
- * A side-effect of setting the <code>Action</code> is that the
- * <code>ActionEvent</code> source's properties are immediately
- * set from the values in the <code>Action</code> (performed by the
- * method <code>configurePropertiesFromAction</code>) and
- * subsequently updated as the <code>Action</code>'s properties change
- * (via a <code>PropertyChangeListener</code> created by the method
- * <code>createActionPropertyChangeListener</code>.
- *
- * @param a the <code>Action</code> for the <code>AbstractButton</code>,
- * or <code>null</code>
- * @since 1.3
- * @see Action
- * @see #getAction
- * @see #configurePropertiesFromAction
- * @see #createActionPropertyChangeListener
- * @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);
- revalidate();
- repaint();
- }
- }
- 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</code> for this
- * <code>ActionEvent</code> source, or <code>null</code>
- * if no <code>Action</code> is set.
- *
- * @return the <code>Action</code> for this <code>ActionEvent</code>
- * source, or <code>null</code>
- * @since 1.3
- * @see Action
- * @see #setAction
- */
- public Action getAction() {
- return action;
- }
- /**
- * Factory method which sets the <code>ActionEvent</code>
- * source's properties according to values from the
- * <code>Action</code> instance. The properties
- * which are set may differ for subclasses. By default,
- * the properties which get set are <code>Text, Icon,
- * Enabled, ToolTipText, ActionCommand</code>, and <code>Mnemonic</code>.
- * <p>
- * If the <code>Action</code> passed in is <code>null</code>,
- * the following things will occur:
- * <ul>
- * <li>the text is set to <code>null</code>,
- * <li>the icon is set to <code>null</code>,
- * <li>enabled is set to true,
- * <li>the tooltip text is set to <code>null</code>
- * </ul>
- *
- * @param a the <code>Action</code> from which to get the properties,
- * or <code>null</code>
- * @since 1.3
- * @see Action
- * @see #setAction
- */
- protected void configurePropertiesFromAction(Action a) {
- configurePropertiesFromAction(a, null);
- }
- /**
- * Configures the AbstractButton's properties according to values
- * from the <code>Action</code> instance. Which properties to set
- * is determined by the <code>types</code> parameter.
- * <code>types</code> may hold the following keys:
- * <ul>
- * <li><code>Action.NAME</code> - set the <code>Text</code> property
- * from the <code>Action</code>,
- * <li><code>Action.SHORT_DESCRIPTION</code> - set the
- * <code>ToolTipText</code> property from the <code>Action</code>,
- * <li><code>Action.SMALL_ICON</code> - set the <code>Icon</code> property
- * from the <code>Action</code>,
- * <li><code>Action.MNEMONIC</code> - set the <code>Mnemonic</code>
- * property from the <code>Action</code>,
- * <li><code>Action.ACTION_COMMAND_KEY</code> - set the
- * <code>ActionCommand</code> property from the <code>Action</code>,
- * <li><code>"enabled"</code> - set <code>Enabled</code> property
- * from the <code>Action</code>
- * </ul>
- * <p>
- * If the <code>Action</code> passed in is <code>null</code>,
- * the following things will occur:
- * <ul>
- * <li>the text is set to <code>null</code>,
- * <li>the icon is set to <code>null</code>,
- * <li>enabled is set to true,
- * <li>the tooltip text is set to <code>null</code>
- * <li>the mnemonic is set to <code>'\0'</code>
- * </ul>
- *
- * @param a the <code>Action</code> from which to get the properties,
- * or <code>null</code>
- * @param types determines which properties to set from the
- * <code>Action</code>
- * @since 1.4
- * @see Action
- * @see #setAction
- * @see #configurePropertiesFromAction(javax.swing.Action)
- */
- void configurePropertiesFromAction(Action a, String[] types) {
- if (types == null) {
- String[] alltypes = { Action.MNEMONIC_KEY, Action.NAME,
- Action.SHORT_DESCRIPTION, Action.SMALL_ICON,
- Action.ACTION_COMMAND_KEY, "enabled" };
- types = alltypes;
- }
- for (int i=0; i<types.length; i++) {
- String type = types[i];
- if (type == null) continue;
- if (type.equals(Action.MNEMONIC_KEY)) {
- Integer n = (a==null) ? null : (Integer)a.getValue(type);
- setMnemonic(n==null ? '\0' : n.intValue());
- } else if (type.equals(Action.NAME)) {
- // When hideActionText property is set, we don't use
- // Action name for button text. Useful for toolbar buttons.
- Boolean hide = (Boolean)getClientProperty("hideActionText");
- setText(a != null && hide!=Boolean.TRUE ?
- (String)a.getValue(Action.NAME) :
- null);
- } else if (type.equals(Action.SHORT_DESCRIPTION)) {
- setToolTipText(a!=null ? (String)a.getValue(type) : null);
- } else if (type.equals(Action.SMALL_ICON)) {
- setIcon(a!=null ? (Icon)a.getValue(type) : null);
- } else if (type.equals(Action.ACTION_COMMAND_KEY)) {
- setActionCommand(a!=null? (String)a.getValue(type) : null);
- } else if (type.equals("enabled")) {
- setEnabled(a!=null ? a.isEnabled() : true);
- }
- }
- }
- /**
- * Factory method which creates the <code>PropertyChangeListener</code>
- * used to update the <code>ActionEvent</code> source as properties
- * change on its <code>Action</code> instance. Subclasses may
- * override this in order to provide their own
- * <code>PropertyChangeListener</code> if the set of
- * properties which should be kept up to date differs from the
- * default properties (<code>Text, Icon, Enabled, ToolTipText,
- * Mnemonic</code>).
- * <p>
- * Note that <code>PropertyChangeListeners</code> should avoid holding
- * strong references to the <code>ActionEvent</code> source,
- * as this may hinder garbage collection of the
- * <code>ActionEvent</code> source and all components
- * in its containment hierarchy.
- *
- * @param a the new action for the button
- * @since 1.3
- * @see Action
- * @see #setAction
- */
- protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
- return new ButtonActionPropertyChangeListener(this, a);
- }
- private static class ButtonActionPropertyChangeListener
- extends AbstractActionPropertyChangeListener
- implements Serializable {
- ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
- super(b, a);
- }
- public void propertyChange(PropertyChangeEvent e) {
- String propertyName = e.getPropertyName();
- AbstractButton button = (AbstractButton)getTarget();
- if (button == null) { //WeakRef GC'ed in 1.2
- Action action = (Action)e.getSource();
- action.removePropertyChangeListener(this);
- } else {
- if (e.getPropertyName().equals(Action.NAME)) {
- Boolean hide = (Boolean)button.getClientProperty("hideActionText");
- if (hide == null || hide == Boolean.FALSE) {
- String text = (String) e.getNewValue();
- button.setText(text);
- button.repaint();
- }
- } else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION)) {
- String text = (String) e.getNewValue();
- button.setToolTipText(text);
- } else if (propertyName.equals("enabled")) {
- Boolean enabledState = (Boolean) e.getNewValue();
- button.setEnabled(enabledState.booleanValue());
- button.repaint();
- } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
- Icon icon = (Icon) e.getNewValue();
- button.setIcon(icon);
- button.invalidate();
- button.repaint();
- } else if (e.getPropertyName().equals(Action.MNEMONIC_KEY)) {
- Integer mn = (Integer) e.getNewValue();
- button.setMnemonic(mn.intValue());
- button.invalidate();
- button.repaint();
- } else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY)) {
- button.setActionCommand((String)e.getNewValue());
- }
- }
- }
- }
- /**
- * Gets the <code>borderPainted</code> property.
- *
- * @return the value of the <code>borderPainted</code> property
- * @see #setBorderPainted
- */
- public boolean isBorderPainted() {
- return paintBorder;
- }
- /**
- * Sets the <code>borderPainted</code> property.
- * If <code>true</code> and the button has a border,
- * the border is painted. The default value for the
- * <code>borderPainted</code> property is <code>true</code>.
- *
- * @param b if true and border property is not <code>null</code>,
- * 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</code>
- * property is true and the button has a border.
- * @param g the <code>Graphics</code> context in which to paint
- *
- * @see #paint
- * @see #setBorder
- */
- protected void paintBorder(Graphics g) {
- if (isBorderPainted()) {
- super.paintBorder(g);
- }
- }
- /**
- * Gets the <code>paintFocus</code> property.
- *
- * @return the <code>paintFocus</code> property
- * @see #setFocusPainted
- */
- public boolean isFocusPainted() {
- return paintFocus;
- }
- /**
- * Sets the <code>paintFocus</code> property, which must
- * be <code>true</code> for the focus state to be painted.
- * The default value for the <code>paintFocus</code> property
- * is <code>true</code>.
- * Some look and feels might not paint focus state;
- * they will ignore this property.
- *
- * @param b if <code>true</code>, 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</code> property.
- *
- * @return the <code>contentAreaFilled</code> property
- * @see #setContentAreaFilled
- */
- public boolean isContentAreaFilled() {
- return contentAreaFilled;
- }
- /**
- * Sets the <code>contentAreaFilled</code> property.
- * If <code>true</code> 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</code>. Do not call <code>setOpaque(false)</code>.
- * The default value for the the <code>contentAreaFilled</code>
- * property is <code>true</code>.
- * <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</code> property.
- *
- * @return the value of the <code>rolloverEnabled</code> property
- * @see #setRolloverEnabled
- */
- public boolean isRolloverEnabled() {
- return rolloverEnabled;
- }
- /**
- * Sets the <code>rolloverEnabled</code> property, which
- * must be <code>true</code> for rollover effects to occur.
- * The default value for the <code>rolloverEnabled</code>
- * property is <code>false</code>.
- * Some look and feels might not implement rollover effects;
- * they will ignore this property.
- *
- * @param b if <code>true</code>, 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</code>
- * keycodes defined in <code>java.awt.event.KeyEvent</code>.
- * 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)</code>
- * 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>A</u>s', you would have to invoke
- * <code>setDisplayedMnemonicIndex(5)</code> after invoking
- * <code>setMnemonic(KeyEvent.VK_A)</code>.
- *
- * @since 1.4
- * @param index Index into the String to underline
- * @exception IllegalArgumentException will be thrown if <code>index</code>
- * 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