/* * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.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.*; 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. *

* Buttons can be configured, and to some degree controlled, by * Actions. Using an * Action with a button has many benefits beyond directly * configuring a button. Refer to * Swing Components Supporting Action for more * details, and you can find more information in How * to Use Actions, a section in The Java Tutorial. *

* For further information see * How to Use Buttons, Check Boxes, and Radio Buttons, * a section in The Java Tutorial. *

* 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 JavaBeansTM * has been added to the 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 changeListener. */ protected ChangeListener changeListener = null; /** * The button model's ActionListener. */ protected ActionListener actionListener = null; /** * The button model's ItemListener. */ protected ItemListener itemListener = null; /** * Only one 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 hideActionText property, which determines * whether the button displays text from the Action. * This is useful only if an Action has been * installed on the button. * * @param hideActionText true if the button's * text property should not reflect * that of the Action; the default is * false * @see Swing Components Supporting * Action * @since 1.6 * @beaninfo * bound: true * expert: true * description: Whether the text of the button should come from * the 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 hideActionText property, which * determines whether the button displays text from the * Action. This is useful only if an Action * has been installed on the button. * * @return true if the button's text * property should not reflect that of the * 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 actionEvent. * Call doClick 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 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 null will cause the button to * use the default margin. The button's default 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 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 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 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 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 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 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 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. *

* Some look and feels might not render the disabled Icon, in which * case they will ignore this. * * @return the disabledIcon 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 * getDisabledIcon() otherwise. *

* Some look and feels might not render the disabled selected Icon, in * which case they will ignore this. * * @return the 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 verticalAlignment property, one of the * following values: *

*/ public int getVerticalAlignment() { return verticalAlignment; } /** * Sets the vertical alignment of the icon and text. * @param alignment one of the following values: * * @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 horizontalAlignment property, * one of the following values: * */ 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: * * @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 verticalTextPosition property, * one of the following values: * */ public int getVerticalTextPosition() { return verticalTextPosition; } /** * Sets the vertical position of the text relative to the icon. * @param textPosition one of the following values: * * @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 horizontalTextPosition property, * one of the following values: * */ public int getHorizontalTextPosition() { return horizontalTextPosition; } /** * Sets the horizontal position of the text relative to the icon. * @param textPosition one of the following values: * * @exception IllegalArgumentException if 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. *

* The default value of this property is 4 pixels. *

* 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: *

* * @param key the property value to check * @param exception the message to use in the * {@code IllegalArgumentException} that is thrown for an invalid * value * @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: * * * @param key the property value to check * @param exception the message to use in the * {@code IllegalArgumentException} that is thrown for an invalid * value * @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 Action. * The new Action replaces any previously set * Action but does not affect ActionListeners * independently added with addActionListener. * If the Action is already a registered * ActionListener for the button, it is not re-registered. *

* Setting the Action results in immediately changing * all the properties described in * Swing Components Supporting Action. * Subsequently, the button's properties are automatically updated * as the Action's properties change. *

* This method uses three other methods to set * and help track the Action's property values. * It uses the configurePropertiesFromAction method * to immediately change the button's properties. * To track changes in the Action's property values, * this method registers the PropertyChangeListener * returned by createActionPropertyChangeListener. The * default {@code PropertyChangeListener} invokes the * {@code actionPropertyChanged} method when a property in the * {@code Action} changes. * * @param a the Action for the AbstractButton, * or 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 Action for this * ActionEvent source, or null * if no Action is set. * * @return the Action for this ActionEvent * source, or 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 * Action. Refer to * Swing Components Supporting Action for more * details as to which properties this sets. * * @param a the Action from which to get the properties, * or 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}. *

* Refer to the table at * Swing Components Supporting Action for a list of * the properties this method sets. * * @param action the 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 = (ButtonGroup) ((DefaultButtonModel)getModel()).getGroup(); if (group != null) { group.clearSelection(); } } } } } /** * Creates and returns a PropertyChangeListener that is * responsible for listening for changes from the specified * Action and updating the appropriate properties. *

* 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 Action. * * @param a the button's action * @since 1.3 * @see Actions * @see Action * @see #setAction */ protected PropertyChangeListener createActionPropertyChangeListener(Action a) { return createActionPropertyChangeListener0(a); } PropertyChangeListener createActionPropertyChangeListener0(Action a) { return new ButtonActionPropertyChangeListener(this, a); } private static class ButtonActionPropertyChangeListener extends ActionPropertyChangeListener { 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 borderPainted property. * * @return the value of the borderPainted property * @see #setBorderPainted */ public boolean isBorderPainted() { return paintBorder; } /** * Sets the borderPainted property. * If true and the button has a border, * the border is painted. The default value for the * borderPainted property is true. * * @param b if true and border property is not 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 BorderPainted * property is true and the button has a border. * @param g the Graphics context in which to paint * * @see #paint * @see #setBorder */ protected void paintBorder(Graphics g) { if (isBorderPainted()) { super.paintBorder(g); } } /** * Gets the paintFocus property. * * @return the paintFocus property * @see #setFocusPainted */ public boolean isFocusPainted() { return paintFocus; } /** * Sets the paintFocus property, which must * be true for the focus state to be painted. * The default value for the paintFocus property * is true. * Some look and feels might not paint focus state; * they will ignore this property. * * @param b if 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 contentAreaFilled property. * * @return the contentAreaFilled property * @see #setContentAreaFilled */ public boolean isContentAreaFilled() { return contentAreaFilled; } /** * Sets the contentAreaFilled property. * If 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 false. Do not call setOpaque(false). * The default value for the the contentAreaFilled * property is true. *

* This function may cause the component's opaque property to change. *

* 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 rolloverEnabled property. * * @return the value of the rolloverEnabled property * @see #setRolloverEnabled */ public boolean isRolloverEnabled() { return rolloverEnabled; } /** * Sets the rolloverEnabled property, which * must be true for rollover effects to occur. * The default value for the rolloverEnabled * property is false. * Some look and feels might not implement rollover effects; * they will ignore this property. * * @param b if 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. *

* A mnemonic must correspond to a single key on the keyboard * and should be specified using one of the VK_XXX * keycodes defined in java.awt.event.KeyEvent. * 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. *

* 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 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. *

* 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 As', you would have to invoke * setDisplayedMnemonicIndex(5) after invoking * setMnemonic(KeyEvent.VK_A). * * @since 1.4 * @param index Index into the String to underline * @exception IllegalArgumentException will be thrown if 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 model property * @see #setModel */ public ButtonModel getModel() { return model; } /** * Sets the model that this button represents. * @param newModel the new 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 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 AbstractButton * should override this to update the UI. For * example, JButton might do the following: *

     *      setUI((ButtonUI)UIManager.getUI(
     *          "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
     * 
*/ 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 -1 * means append to the end * @exception IllegalArgumentException if 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 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 ChangeListeners added * to this AbstractButton with addChangeListener(). * * @return all of the ChangeListeners added or an empty * array if no listeners have been added * @since 1.4 */ public ChangeListener[] getChangeListeners() { return (ChangeListener[])(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 ActionListener to the button. * @param l the ActionListener to be added */ public void addActionListener(ActionListener l) { listenerList.add(ActionListener.class, l); } /** * Removes an ActionListener from the button. * If the listener is the currently set Action * for the button, then the Action * is set to 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 ActionListeners added * to this AbstractButton with addActionListener(). * * @return all of the ActionListeners added or an empty * array if no listeners have been added * @since 1.4 */ public ActionListener[] getActionListeners() { return (ActionListener[])(listenerList.getListeners( ActionListener.class)); } /** * Subclasses that want to handle ChangeEvents differently * can override this to return another ChangeListener * implementation. * * @return the new ChangeListener */ protected ChangeListener createChangeListener() { return getHandler(); } /** * Extends ChangeListener to be serializable. *

* 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 JavaBeansTM * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. */ 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 event * parameter. * * @param event the 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 event parameter. * * @param event the 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, new Integer(0), new Integer(1)); } else { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.SELECTED, null); accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, new Integer(1), new Integer(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 String containing the label * @deprecated - Replaced by getText */ @Deprecated public String getLabel() { return getText(); } /** * Sets the label text. * * @param label a String containing the text * @deprecated - Replaced by setText(text) * @beaninfo * bound: true * description: Replace by setText(text) */ @Deprecated public void setLabel(String label) { setText(label); } /** * Adds an ItemListener to the checkbox. * @param l the ItemListener to be added */ public void addItemListener(ItemListener l) { listenerList.add(ItemListener.class, l); } /** * Removes an ItemListener from the button. * @param l the ItemListener to be removed */ public void removeItemListener(ItemListener l) { listenerList.remove(ItemListener.class, l); } /** * Returns an array of all the ItemListeners added * to this AbstractButton with addItemListener(). * * @return all of the ItemListeners added or an empty * array if no listeners have been added * @since 1.4 */ public ItemListener[] getItemListeners() { return (ItemListener[])listenerList.getListeners(ItemListener.class); } /** * Returns an array (length 1) containing the label or * null if the button is not selected. * * @return an array containing 1 Object: the text of the button, * if the item is selected; otherwise 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 Icon's * Image is not equal to the * passed in Image img. * * @param img the 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 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 null. *

* Overriding paramString to provide information about the * specific new aspects of the JFC components. * * @return a string representation of this 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 // 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 * AbstractButton class. It provides an implementation of the * Java Accessibility API appropriate to button and menu item * user-interface elements. *

* 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 JavaBeansTM * has been added to the 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 * 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 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 new Integer(1); } else { return new Integer(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 new Integer(0); } /** * Get the maximum value of this object as a Number. * * @return An Integer of 1. */ public Number getMaximumAccessibleValue() { return new Integer(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 teh 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( (JComponent)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: * * Component c = * 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; * * } * } * * * @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); } } } }