Path: blob/master/src/java.desktop/share/classes/javax/swing/AbstractButton.java
41153 views
/*1* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package javax.swing;2526import java.awt.*;27import java.awt.event.*;28import java.text.*;29import java.awt.geom.*;30import java.beans.JavaBean;31import java.beans.BeanProperty;32import java.beans.PropertyChangeEvent;33import java.beans.PropertyChangeListener;34import java.beans.Transient;35import java.util.Enumeration;36import java.io.Serializable;37import javax.swing.event.*;38import javax.swing.plaf.*;39import javax.accessibility.*;40import javax.swing.text.*;4142/**43* Defines common behaviors for buttons and menu items.44* <p>45* Buttons can be configured, and to some degree controlled, by46* <code><a href="Action.html">Action</a></code>s. Using an47* <code>Action</code> with a button has many benefits beyond directly48* configuring a button. Refer to <a href="Action.html#buttonActions">49* Swing Components Supporting <code>Action</code></a> for more50* details, and you can find more information in <a51* href="https://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How52* to Use Actions</a>, a section in <em>The Java Tutorial</em>.53* <p>54* For further information see55* <a56href="https://docs.oracle.com/javase/tutorial/uiswing/components/button.html">How to Use Buttons, Check Boxes, and Radio Buttons</a>,57* a section in <em>The Java Tutorial</em>.58* <p>59* <strong>Warning:</strong>60* Serialized objects of this class will not be compatible with61* future Swing releases. The current serialization support is62* appropriate for short term storage or RMI between applications running63* the same version of Swing. As of 1.4, support for long term storage64* of all JavaBeans65* has been added to the <code>java.beans</code> package.66* Please see {@link java.beans.XMLEncoder}.67*68* @author Jeff Dinkins69* @since 1.270*/71@JavaBean(defaultProperty = "UI")72@SuppressWarnings("serial") // Same-version serialization only73public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {7475// *********************************76// ******* Button properties *******77// *********************************7879/** Identifies a change in the button model. */80public static final String MODEL_CHANGED_PROPERTY = "model";81/** Identifies a change in the button's text. */82public static final String TEXT_CHANGED_PROPERTY = "text";83/** Identifies a change to the button's mnemonic. */84public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";8586// Text positioning and alignment87/** Identifies a change in the button's margins. */88public static final String MARGIN_CHANGED_PROPERTY = "margin";89/** Identifies a change in the button's vertical alignment. */90public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = "verticalAlignment";91/** Identifies a change in the button's horizontal alignment. */92public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = "horizontalAlignment";9394/** Identifies a change in the button's vertical text position. */95public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = "verticalTextPosition";96/** Identifies a change in the button's horizontal text position. */97public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = "horizontalTextPosition";9899// Paint options100/**101* Identifies a change to having the border drawn,102* or having it not drawn.103*/104public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";105/**106* Identifies a change to having the border highlighted when focused,107* or not.108*/109public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";110/**111* Identifies a change from rollover enabled to disabled or back112* to enabled.113*/114public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = "rolloverEnabled";115/**116* Identifies a change to having the button paint the content area.117*/118public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = "contentAreaFilled";119120// Icons121/** Identifies a change to the icon that represents the button. */122public static final String ICON_CHANGED_PROPERTY = "icon";123124/**125* Identifies a change to the icon used when the button has been126* pressed.127*/128public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";129/**130* Identifies a change to the icon used when the button has131* been selected.132*/133public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";134135/**136* Identifies a change to the icon used when the cursor is over137* the button.138*/139public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";140/**141* Identifies a change to the icon used when the cursor is142* over the button and it has been selected.143*/144public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = "rolloverSelectedIcon";145146/**147* Identifies a change to the icon used when the button has148* been disabled.149*/150public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";151/**152* Identifies a change to the icon used when the button has been153* disabled and selected.154*/155public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = "disabledSelectedIcon";156157158/** The data model that determines the button's state. */159protected ButtonModel model = null;160161private String text = ""; // for BeanBox162private Insets margin = null;163private Insets defaultMargin = null;164165// Button icons166// PENDING(jeff) - hold icons in an array167private Icon defaultIcon = null;168private Icon pressedIcon = null;169private Icon disabledIcon = null;170171private Icon selectedIcon = null;172private Icon disabledSelectedIcon = null;173174private Icon rolloverIcon = null;175private Icon rolloverSelectedIcon = null;176177// Display properties178private boolean paintBorder = true;179private boolean paintFocus = true;180private boolean rolloverEnabled = false;181private boolean contentAreaFilled = true;182183// Icon/Label Alignment184private int verticalAlignment = CENTER;185private int horizontalAlignment = CENTER;186187private int verticalTextPosition = CENTER;188private int horizontalTextPosition = TRAILING;189190private int iconTextGap = 4;191192private int mnemonic;193private int mnemonicIndex = -1;194195private long multiClickThreshhold = 0;196197private boolean borderPaintedSet = false;198private boolean rolloverEnabledSet = false;199private boolean iconTextGapSet = false;200private boolean contentAreaFilledSet = false;201202// Whether or not we've set the LayoutManager.203private boolean setLayout = false;204205// This is only used by JButton, promoted to avoid an extra206// boolean field in JButton207boolean defaultCapable = true;208209/**210* Combined listeners: ActionListener, ChangeListener, ItemListener.211*/212private Handler handler;213214/**215* The button model's <code>changeListener</code>.216*/217protected ChangeListener changeListener = null;218/**219* The button model's <code>ActionListener</code>.220*/221protected ActionListener actionListener = null;222/**223* The button model's <code>ItemListener</code>.224*/225protected ItemListener itemListener = null;226227/**228* Only one <code>ChangeEvent</code> is needed per button229* instance since the230* event's only state is the source property. The source of events231* generated is always "this".232*/233protected transient ChangeEvent changeEvent;234235private boolean hideActionText = false;236237/**238* Constructor for subclasses to call.239*/240protected AbstractButton() {}241242/**243* Sets the <code>hideActionText</code> property, which determines244* whether the button displays text from the <code>Action</code>.245* This is useful only if an <code>Action</code> has been246* installed on the button.247*248* @param hideActionText <code>true</code> if the button's249* <code>text</code> property should not reflect250* that of the <code>Action</code>; the default is251* <code>false</code>252* @see <a href="Action.html#buttonActions">Swing Components Supporting253* <code>Action</code></a>254* @since 1.6255*/256@BeanProperty(expert = true, description257= "Whether the text of the button should come from the <code>Action</code>.")258public void setHideActionText(boolean hideActionText) {259if (hideActionText != this.hideActionText) {260this.hideActionText = hideActionText;261if (getAction() != null) {262setTextFromAction(getAction(), false);263}264firePropertyChange("hideActionText", !hideActionText,265hideActionText);266}267}268269/**270* Returns the value of the <code>hideActionText</code> property, which271* determines whether the button displays text from the272* <code>Action</code>. This is useful only if an <code>Action</code>273* has been installed on the button.274*275* @return <code>true</code> if the button's <code>text</code>276* property should not reflect that of the277* <code>Action</code>; the default is <code>false</code>278* @since 1.6279*/280public boolean getHideActionText() {281return hideActionText;282}283284/**285* Returns the button's text.286* @return the buttons text287* @see #setText288*/289public String getText() {290return text;291}292293/**294* Sets the button's text.295* @param text the string used to set the text296* @see #getText297*/298@BeanProperty(preferred = true, visualUpdate = true, description299= "The button's text.")300public void setText(String text) {301String oldValue = this.text;302this.text = text;303firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);304updateDisplayedMnemonicIndex(text, getMnemonic());305306if (accessibleContext != null) {307accessibleContext.firePropertyChange(308AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,309oldValue, text);310}311if (text == null || oldValue == null || !text.equals(oldValue)) {312revalidate();313repaint();314}315}316317318/**319* Returns the state of the button. True if the320* toggle button is selected, false if it's not.321* @return true if the toggle button is selected, otherwise false322*/323public boolean isSelected() {324return model.isSelected();325}326327/**328* Sets the state of the button. Note that this method does not329* trigger an <code>actionEvent</code>.330* Call <code>doClick</code> to perform a programmatic action change.331*332* @param b true if the button is selected, otherwise false333*/334public void setSelected(boolean b) {335boolean oldValue = isSelected();336337// TIGER - 4840653338// Removed code which fired an AccessibleState.SELECTED339// PropertyChangeEvent since this resulted in two340// identical events being fired since341// AbstractButton.fireItemStateChanged also fires the342// same event. This caused screen readers to speak the343// name of the item twice.344345model.setSelected(b);346}347348/**349* Programmatically perform a "click". This does the same350* thing as if the user had pressed and released the button.351*/352public void doClick() {353doClick(68);354}355356/**357* Programmatically perform a "click". This does the same358* thing as if the user had pressed and released the button.359* The button stays visually "pressed" for <code>pressTime</code>360* milliseconds.361*362* @param pressTime the time to "hold down" the button, in milliseconds363*/364public void doClick(int pressTime) {365Dimension size = getSize();366model.setArmed(true);367model.setPressed(true);368paintImmediately(new Rectangle(0,0, size.width, size.height));369try {370Thread.sleep(pressTime);371} catch(InterruptedException ie) {372}373model.setPressed(false);374model.setArmed(false);375}376377/**378* Sets space for margin between the button's border and379* the label. Setting to <code>null</code> will cause the button to380* use the default margin. The button's default <code>Border</code>381* object will use this value to create the proper margin.382* However, if a non-default border is set on the button,383* it is that <code>Border</code> object's responsibility to create the384* appropriate margin space (else this property will385* effectively be ignored).386*387* @param m the space between the border and the label388*/389@BeanProperty(visualUpdate = true, description390= "The space between the button's border and the label.")391public void setMargin(Insets m) {392// Cache the old margin if it comes from the UI393if(m instanceof UIResource) {394defaultMargin = m;395} else if(margin instanceof UIResource) {396defaultMargin = margin;397}398399// If the client passes in a null insets, restore the margin400// from the UI if possible401if(m == null && defaultMargin != null) {402m = defaultMargin;403}404405Insets old = margin;406margin = m;407firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);408if (old == null || !old.equals(m)) {409revalidate();410repaint();411}412}413414/**415* Returns the margin between the button's border and416* the label.417*418* @return an <code>Insets</code> object specifying the margin419* between the botton's border and the label420* @see #setMargin421*/422public Insets getMargin() {423return (margin == null) ? null : (Insets) margin.clone();424}425426/**427* Returns the default icon.428* @return the default <code>Icon</code>429* @see #setIcon430*/431public Icon getIcon() {432return defaultIcon;433}434435/**436* Sets the button's default icon. This icon is437* also used as the "pressed" and "disabled" icon if438* there is no explicitly set pressed icon.439*440* @param defaultIcon the icon used as the default image441* @see #getIcon442* @see #setPressedIcon443*/444@BeanProperty(visualUpdate = true, description445= "The button's default icon")446public void setIcon(Icon defaultIcon) {447Icon oldValue = this.defaultIcon;448this.defaultIcon = defaultIcon;449450/* If the default icon has really changed and we had451* generated the disabled icon for this component,452* (i.e. setDisabledIcon() was never called) then453* clear the disabledIcon field.454*/455if (defaultIcon != oldValue && (disabledIcon instanceof UIResource)) {456disabledIcon = null;457}458459firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);460if (accessibleContext != null) {461accessibleContext.firePropertyChange(462AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,463oldValue, defaultIcon);464}465if (defaultIcon != oldValue) {466if (defaultIcon == null || oldValue == null ||467defaultIcon.getIconWidth() != oldValue.getIconWidth() ||468defaultIcon.getIconHeight() != oldValue.getIconHeight()) {469revalidate();470}471repaint();472}473}474475/**476* Returns the pressed icon for the button.477* @return the <code>pressedIcon</code> property478* @see #setPressedIcon479*/480public Icon getPressedIcon() {481return pressedIcon;482}483484/**485* Sets the pressed icon for the button.486* @param pressedIcon the icon used as the "pressed" image487* @see #getPressedIcon488*/489@BeanProperty(visualUpdate = true, description490= "The pressed icon for the button.")491public void setPressedIcon(Icon pressedIcon) {492Icon oldValue = this.pressedIcon;493this.pressedIcon = pressedIcon;494firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);495if (accessibleContext != null) {496accessibleContext.firePropertyChange(497AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,498oldValue, pressedIcon);499}500if (pressedIcon != oldValue) {501if (getModel().isPressed()) {502repaint();503}504}505}506507/**508* Returns the selected icon for the button.509* @return the <code>selectedIcon</code> property510* @see #setSelectedIcon511*/512public Icon getSelectedIcon() {513return selectedIcon;514}515516/**517* Sets the selected icon for the button.518* @param selectedIcon the icon used as the "selected" image519* @see #getSelectedIcon520*/521@BeanProperty(visualUpdate = true, description522= "The selected icon for the button.")523public void setSelectedIcon(Icon selectedIcon) {524Icon oldValue = this.selectedIcon;525this.selectedIcon = selectedIcon;526527/* If the default selected icon has really changed and we had528* generated the disabled selected icon for this component,529* (i.e. setDisabledSelectedIcon() was never called) then530* clear the disabledSelectedIcon field.531*/532if (selectedIcon != oldValue &&533disabledSelectedIcon instanceof UIResource) {534535disabledSelectedIcon = null;536}537538firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);539if (accessibleContext != null) {540accessibleContext.firePropertyChange(541AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,542oldValue, selectedIcon);543}544if (selectedIcon != oldValue) {545if (isSelected()) {546repaint();547}548}549}550551/**552* Returns the rollover icon for the button.553* @return the <code>rolloverIcon</code> property554* @see #setRolloverIcon555*/556public Icon getRolloverIcon() {557return rolloverIcon;558}559560/**561* Sets the rollover icon for the button.562* @param rolloverIcon the icon used as the "rollover" image563* @see #getRolloverIcon564*/565@BeanProperty(visualUpdate = true, description566= "The rollover icon for the button.")567public void setRolloverIcon(Icon rolloverIcon) {568Icon oldValue = this.rolloverIcon;569this.rolloverIcon = rolloverIcon;570firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);571if (accessibleContext != null) {572accessibleContext.firePropertyChange(573AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,574oldValue, rolloverIcon);575}576setRolloverEnabled(true);577if (rolloverIcon != oldValue) {578// No way to determine whether we are currently in579// a rollover state, so repaint regardless580repaint();581}582583}584585/**586* Returns the rollover selection icon for the button.587* @return the <code>rolloverSelectedIcon</code> property588* @see #setRolloverSelectedIcon589*/590public Icon getRolloverSelectedIcon() {591return rolloverSelectedIcon;592}593594/**595* Sets the rollover selected icon for the button.596* @param rolloverSelectedIcon the icon used as the597* "selected rollover" image598* @see #getRolloverSelectedIcon599*/600@BeanProperty(visualUpdate = true, description601= "The rollover selected icon for the button.")602public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {603Icon oldValue = this.rolloverSelectedIcon;604this.rolloverSelectedIcon = rolloverSelectedIcon;605firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);606if (accessibleContext != null) {607accessibleContext.firePropertyChange(608AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,609oldValue, rolloverSelectedIcon);610}611setRolloverEnabled(true);612if (rolloverSelectedIcon != oldValue) {613// No way to determine whether we are currently in614// a rollover state, so repaint regardless615if (isSelected()) {616repaint();617}618}619}620621/**622* Returns the icon used by the button when it's disabled.623* If no disabled icon has been set this will forward the call to624* the look and feel to construct an appropriate disabled Icon.625* <p>626* Some look and feels might not render the disabled Icon, in which627* case they will ignore this.628*629* @return the <code>disabledIcon</code> property630* @see #getPressedIcon631* @see #setDisabledIcon632* @see javax.swing.LookAndFeel#getDisabledIcon633*/634@Transient635public Icon getDisabledIcon() {636if (disabledIcon == null) {637disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());638if (disabledIcon != null) {639firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);640}641}642return disabledIcon;643}644645/**646* Sets the disabled icon for the button.647* @param disabledIcon the icon used as the disabled image648* @see #getDisabledIcon649*/650@BeanProperty(visualUpdate = true, description651= "The disabled icon for the button.")652public void setDisabledIcon(Icon disabledIcon) {653Icon oldValue = this.disabledIcon;654this.disabledIcon = disabledIcon;655firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);656if (accessibleContext != null) {657accessibleContext.firePropertyChange(658AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,659oldValue, disabledIcon);660}661if (disabledIcon != oldValue) {662if (!isEnabled()) {663repaint();664}665}666}667668/**669* Returns the icon used by the button when it's disabled and selected.670* If no disabled selection icon has been set, this will forward671* the call to the LookAndFeel to construct an appropriate disabled672* Icon from the selection icon if it has been set and to673* <code>getDisabledIcon()</code> otherwise.674* <p>675* Some look and feels might not render the disabled selected Icon, in676* which case they will ignore this.677*678* @return the <code>disabledSelectedIcon</code> property679* @see #getDisabledIcon680* @see #setDisabledSelectedIcon681* @see javax.swing.LookAndFeel#getDisabledSelectedIcon682*/683public Icon getDisabledSelectedIcon() {684if (disabledSelectedIcon == null) {685if (selectedIcon != null) {686disabledSelectedIcon = UIManager.getLookAndFeel().687getDisabledSelectedIcon(this, getSelectedIcon());688} else {689return getDisabledIcon();690}691}692return disabledSelectedIcon;693}694695/**696* Sets the disabled selection icon for the button.697* @param disabledSelectedIcon the icon used as the disabled698* selection image699* @see #getDisabledSelectedIcon700*/701@BeanProperty(visualUpdate = true, description702= "The disabled selection icon for the button.")703public void setDisabledSelectedIcon(Icon disabledSelectedIcon) {704Icon oldValue = this.disabledSelectedIcon;705this.disabledSelectedIcon = disabledSelectedIcon;706firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, oldValue, disabledSelectedIcon);707if (accessibleContext != null) {708accessibleContext.firePropertyChange(709AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,710oldValue, disabledSelectedIcon);711}712if (disabledSelectedIcon != oldValue) {713if (disabledSelectedIcon == null || oldValue == null ||714disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||715disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {716revalidate();717}718if (!isEnabled() && isSelected()) {719repaint();720}721}722}723724/**725* Returns the vertical alignment of the text and icon.726*727* @return the <code>verticalAlignment</code> property, one of the728* following values:729* <ul>730* <li>{@code SwingConstants.CENTER} (the default)731* <li>{@code SwingConstants.TOP}732* <li>{@code SwingConstants.BOTTOM}733* </ul>734*/735public int getVerticalAlignment() {736return verticalAlignment;737}738739/**740* Sets the vertical alignment of the icon and text.741* @param alignment one of the following values:742* <ul>743* <li>{@code SwingConstants.CENTER} (the default)744* <li>{@code SwingConstants.TOP}745* <li>{@code SwingConstants.BOTTOM}746* </ul>747* @throws IllegalArgumentException if the alignment is not one of the legal748* values listed above749*/750@BeanProperty(visualUpdate = true, enumerationValues = {751"SwingConstants.TOP",752"SwingConstants.CENTER",753"SwingConstants.BOTTOM"}, description754= "The vertical alignment of the icon and text.")755public void setVerticalAlignment(int alignment) {756if (alignment == verticalAlignment) return;757int oldValue = verticalAlignment;758verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");759firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment); repaint();760}761762/**763* Returns the horizontal alignment of the icon and text.764* {@code AbstractButton}'s default is {@code SwingConstants.CENTER},765* but subclasses such as {@code JCheckBox} may use a different default.766*767* @return the <code>horizontalAlignment</code> property,768* one of the following values:769* <ul>770* <li>{@code SwingConstants.RIGHT}771* <li>{@code SwingConstants.LEFT}772* <li>{@code SwingConstants.CENTER}773* <li>{@code SwingConstants.LEADING}774* <li>{@code SwingConstants.TRAILING}775* </ul>776*/777public int getHorizontalAlignment() {778return horizontalAlignment;779}780781/**782* Sets the horizontal alignment of the icon and text.783* {@code AbstractButton}'s default is {@code SwingConstants.CENTER},784* but subclasses such as {@code JCheckBox} may use a different default.785*786* @param alignment the alignment value, one of the following values:787* <ul>788* <li>{@code SwingConstants.RIGHT}789* <li>{@code SwingConstants.LEFT}790* <li>{@code SwingConstants.CENTER}791* <li>{@code SwingConstants.LEADING}792* <li>{@code SwingConstants.TRAILING}793* </ul>794* @throws IllegalArgumentException if the alignment is not one of the795* valid values796*/797@BeanProperty(visualUpdate = true, enumerationValues = {798"SwingConstants.LEFT",799"SwingConstants.CENTER",800"SwingConstants.RIGHT",801"SwingConstants.LEADING",802"SwingConstants.TRAILING"}, description803= "The horizontal alignment of the icon and text.")804public void setHorizontalAlignment(int alignment) {805if (alignment == horizontalAlignment) return;806int oldValue = horizontalAlignment;807horizontalAlignment = checkHorizontalKey(alignment,808"horizontalAlignment");809firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,810oldValue, horizontalAlignment);811repaint();812}813814815/**816* Returns the vertical position of the text relative to the icon.817* @return the <code>verticalTextPosition</code> property,818* one of the following values:819* <ul>820* <li>{@code SwingConstants.CENTER} (the default)821* <li>{@code SwingConstants.TOP}822* <li>{@code SwingConstants.BOTTOM}823* </ul>824*/825public int getVerticalTextPosition() {826return verticalTextPosition;827}828829/**830* Sets the vertical position of the text relative to the icon.831* @param textPosition one of the following values:832* <ul>833* <li>{@code SwingConstants.CENTER} (the default)834* <li>{@code SwingConstants.TOP}835* <li>{@code SwingConstants.BOTTOM}836* </ul>837*/838@BeanProperty(visualUpdate = true, enumerationValues = {839"SwingConstants.TOP",840"SwingConstants.CENTER",841"SwingConstants.BOTTOM"}, description842= "The vertical position of the text relative to the icon.")843public void setVerticalTextPosition(int textPosition) {844if (textPosition == verticalTextPosition) return;845int oldValue = verticalTextPosition;846verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");847firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);848revalidate();849repaint();850}851852/**853* Returns the horizontal position of the text relative to the icon.854* @return the <code>horizontalTextPosition</code> property,855* one of the following values:856* <ul>857* <li>{@code SwingConstants.RIGHT}858* <li>{@code SwingConstants.LEFT}859* <li>{@code SwingConstants.CENTER}860* <li>{@code SwingConstants.LEADING}861* <li>{@code SwingConstants.TRAILING} (the default)862* </ul>863*/864public int getHorizontalTextPosition() {865return horizontalTextPosition;866}867868/**869* Sets the horizontal position of the text relative to the icon.870* @param textPosition one of the following values:871* <ul>872* <li>{@code SwingConstants.RIGHT}873* <li>{@code SwingConstants.LEFT}874* <li>{@code SwingConstants.CENTER}875* <li>{@code SwingConstants.LEADING}876* <li>{@code SwingConstants.TRAILING} (the default)877* </ul>878* @exception IllegalArgumentException if <code>textPosition</code>879* is not one of the legal values listed above880*/881@BeanProperty(visualUpdate = true, enumerationValues = {882"SwingConstants.LEFT",883"SwingConstants.CENTER",884"SwingConstants.RIGHT",885"SwingConstants.LEADING",886"SwingConstants.TRAILING"}, description887= "The horizontal position of the text relative to the icon.")888public void setHorizontalTextPosition(int textPosition) {889if (textPosition == horizontalTextPosition) return;890int oldValue = horizontalTextPosition;891horizontalTextPosition = checkHorizontalKey(textPosition,892"horizontalTextPosition");893firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,894oldValue,895horizontalTextPosition);896revalidate();897repaint();898}899900/**901* Returns the amount of space between the text and the icon902* displayed in this button.903*904* @return an int equal to the number of pixels between the text905* and the icon.906* @since 1.4907* @see #setIconTextGap908*/909public int getIconTextGap() {910return iconTextGap;911}912913/**914* If both the icon and text properties are set, this property915* defines the space between them.916* <p>917* The default value of this property is 4 pixels.918* <p>919* This is a JavaBeans bound property.920*921* @param iconTextGap the space between icon and text if these properties are set.922* @since 1.4923* @see #getIconTextGap924*/925@BeanProperty(visualUpdate = true, description926= "If both the icon and text properties are set, this property defines the space between them.")927public void setIconTextGap(int iconTextGap) {928int oldValue = this.iconTextGap;929this.iconTextGap = iconTextGap;930iconTextGapSet = true;931firePropertyChange("iconTextGap", oldValue, iconTextGap);932if (iconTextGap != oldValue) {933revalidate();934repaint();935}936}937938/**939* Verify that the {@code key} argument is a legal value for the940* {@code horizontalAlignment} and {@code horizontalTextPosition}941* properties. Valid values are:942* <ul>943* <li>{@code SwingConstants.RIGHT}944* <li>{@code SwingConstants.LEFT}945* <li>{@code SwingConstants.CENTER}946* <li>{@code SwingConstants.LEADING}947* <li>{@code SwingConstants.TRAILING}948* </ul>949*950* @param key the property value to check951* @param exception the message to use in the952* {@code IllegalArgumentException} that is thrown for an invalid953* value954* @return the {@code key} argument955* @exception IllegalArgumentException if key is not one of the legal956* values listed above957* @see #setHorizontalTextPosition958* @see #setHorizontalAlignment959*/960protected int checkHorizontalKey(int key, String exception) {961if ((key == LEFT) ||962(key == CENTER) ||963(key == RIGHT) ||964(key == LEADING) ||965(key == TRAILING)) {966return key;967} else {968throw new IllegalArgumentException(exception);969}970}971972/**973* Verify that the {@code key} argument is a legal value for the974* vertical properties. Valid values are:975* <ul>976* <li>{@code SwingConstants.CENTER}977* <li>{@code SwingConstants.TOP}978* <li>{@code SwingConstants.BOTTOM}979* </ul>980*981* @param key the property value to check982* @param exception the message to use in the983* {@code IllegalArgumentException} that is thrown for an invalid984* value985* @return the {@code key} argument986* @exception IllegalArgumentException if key is not one of the legal987* values listed above988*/989protected int checkVerticalKey(int key, String exception) {990if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {991return key;992} else {993throw new IllegalArgumentException(exception);994}995}996997/**998*{@inheritDoc}999*1000* @since 1.61001*/1002public void removeNotify() {1003super.removeNotify();1004if(isRolloverEnabled()) {1005getModel().setRollover(false);1006}1007}10081009/**1010* Sets the action command for this button.1011* @param actionCommand the action command for this button1012*/1013public void setActionCommand(String actionCommand) {1014getModel().setActionCommand(actionCommand);1015}10161017/**1018* Returns the action command for this button.1019* @return the action command for this button1020*/1021public String getActionCommand() {1022String ac = getModel().getActionCommand();1023if(ac == null) {1024ac = getText();1025}1026return ac;1027}10281029private Action action;1030private PropertyChangeListener actionPropertyChangeListener;10311032/**1033* Sets the <code>Action</code>.1034* The new <code>Action</code> replaces any previously set1035* <code>Action</code> but does not affect <code>ActionListeners</code>1036* independently added with <code>addActionListener</code>.1037* If the <code>Action</code> is already a registered1038* <code>ActionListener</code> for the button, it is not re-registered.1039* <p>1040* Setting the <code>Action</code> results in immediately changing1041* all the properties described in <a href="Action.html#buttonActions">1042* Swing Components Supporting <code>Action</code></a>.1043* Subsequently, the button's properties are automatically updated1044* as the <code>Action</code>'s properties change.1045* <p>1046* This method uses three other methods to set1047* and help track the <code>Action</code>'s property values.1048* It uses the <code>configurePropertiesFromAction</code> method1049* to immediately change the button's properties.1050* To track changes in the <code>Action</code>'s property values,1051* this method registers the <code>PropertyChangeListener</code>1052* returned by <code>createActionPropertyChangeListener</code>. The1053* default {@code PropertyChangeListener} invokes the1054* {@code actionPropertyChanged} method when a property in the1055* {@code Action} changes.1056*1057* @param a the <code>Action</code> for the <code>AbstractButton</code>,1058* or <code>null</code>1059* @since 1.31060* @see Action1061* @see #getAction1062* @see #configurePropertiesFromAction1063* @see #createActionPropertyChangeListener1064* @see #actionPropertyChanged1065*/1066@BeanProperty(visualUpdate = true, description1067= "the Action instance connected with this ActionEvent source")1068public void setAction(Action a) {1069Action oldValue = getAction();1070if (action==null || !action.equals(a)) {1071action = a;1072if (oldValue!=null) {1073removeActionListener(oldValue);1074oldValue.removePropertyChangeListener(actionPropertyChangeListener);1075actionPropertyChangeListener = null;1076}1077configurePropertiesFromAction(action);1078if (action!=null) {1079// Don't add if it is already a listener1080if (!isListener(ActionListener.class, action)) {1081addActionListener(action);1082}1083// Reverse linkage:1084actionPropertyChangeListener = createActionPropertyChangeListener(action);1085action.addPropertyChangeListener(actionPropertyChangeListener);1086}1087firePropertyChange("action", oldValue, action);1088}1089}10901091private boolean isListener(Class<?> c, ActionListener a) {1092boolean isListener = false;1093Object[] listeners = listenerList.getListenerList();1094for (int i = listeners.length-2; i>=0; i-=2) {1095if (listeners[i]==c && listeners[i+1]==a) {1096isListener=true;1097}1098}1099return isListener;1100}11011102/**1103* Returns the currently set <code>Action</code> for this1104* <code>ActionEvent</code> source, or <code>null</code>1105* if no <code>Action</code> is set.1106*1107* @return the <code>Action</code> for this <code>ActionEvent</code>1108* source, or <code>null</code>1109* @since 1.31110* @see Action1111* @see #setAction1112*/1113public Action getAction() {1114return action;1115}11161117/**1118* Sets the properties on this button to match those in the specified1119* <code>Action</code>. Refer to <a href="Action.html#buttonActions">1120* Swing Components Supporting <code>Action</code></a> for more1121* details as to which properties this sets.1122*1123* @param a the <code>Action</code> from which to get the properties,1124* or <code>null</code>1125* @since 1.31126* @see Action1127* @see #setAction1128*/1129protected void configurePropertiesFromAction(Action a) {1130setMnemonicFromAction(a);1131setTextFromAction(a, false);1132AbstractAction.setToolTipTextFromAction(this, a);1133setIconFromAction(a);1134setActionCommandFromAction(a);1135AbstractAction.setEnabledFromAction(this, a);1136if (AbstractAction.hasSelectedKey(a) &&1137shouldUpdateSelectedStateFromAction()) {1138setSelectedFromAction(a);1139}1140setDisplayedMnemonicIndexFromAction(a, false);1141}11421143void clientPropertyChanged(Object key, Object oldValue,1144Object newValue) {1145if (key == "hideActionText") {1146boolean current = (newValue instanceof Boolean) ?1147(Boolean)newValue : false;1148if (getHideActionText() != current) {1149setHideActionText(current);1150}1151}1152}11531154/**1155* Button subclasses that support mirroring the selected state from1156* the action should override this to return true. AbstractButton's1157* implementation returns false.1158*/1159boolean shouldUpdateSelectedStateFromAction() {1160return false;1161}11621163/**1164* Updates the button's state in response to property changes in the1165* associated action. This method is invoked from the1166* {@code PropertyChangeListener} returned from1167* {@code createActionPropertyChangeListener}. Subclasses do not normally1168* need to invoke this. Subclasses that support additional {@code Action}1169* properties should override this and1170* {@code configurePropertiesFromAction}.1171* <p>1172* Refer to the table at <a href="Action.html#buttonActions">1173* Swing Components Supporting <code>Action</code></a> for a list of1174* the properties this method sets.1175*1176* @param action the <code>Action</code> associated with this button1177* @param propertyName the name of the property that changed1178* @since 1.61179* @see Action1180* @see #configurePropertiesFromAction1181*/1182protected void actionPropertyChanged(Action action, String propertyName) {1183if (propertyName == Action.NAME) {1184setTextFromAction(action, true);1185} else if (propertyName == "enabled") {1186AbstractAction.setEnabledFromAction(this, action);1187} else if (propertyName == Action.SHORT_DESCRIPTION) {1188AbstractAction.setToolTipTextFromAction(this, action);1189} else if (propertyName == Action.SMALL_ICON) {1190smallIconChanged(action);1191} else if (propertyName == Action.MNEMONIC_KEY) {1192setMnemonicFromAction(action);1193} else if (propertyName == Action.ACTION_COMMAND_KEY) {1194setActionCommandFromAction(action);1195} else if (propertyName == Action.SELECTED_KEY &&1196AbstractAction.hasSelectedKey(action) &&1197shouldUpdateSelectedStateFromAction()) {1198setSelectedFromAction(action);1199} else if (propertyName == Action.DISPLAYED_MNEMONIC_INDEX_KEY) {1200setDisplayedMnemonicIndexFromAction(action, true);1201} else if (propertyName == Action.LARGE_ICON_KEY) {1202largeIconChanged(action);1203}1204}12051206private void setDisplayedMnemonicIndexFromAction(1207Action a, boolean fromPropertyChange) {1208Integer iValue = (a == null) ? null :1209(Integer)a.getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY);1210if (fromPropertyChange || iValue != null) {1211int value;1212if (iValue == null) {1213value = -1;1214} else {1215value = iValue;1216String text = getText();1217if (text == null || value >= text.length()) {1218value = -1;1219}1220}1221setDisplayedMnemonicIndex(value);1222}1223}12241225private void setMnemonicFromAction(Action a) {1226Integer n = (a == null) ? null :1227(Integer)a.getValue(Action.MNEMONIC_KEY);1228setMnemonic((n == null) ? '\0' : n);1229}12301231private void setTextFromAction(Action a, boolean propertyChange) {1232boolean hideText = getHideActionText();1233if (!propertyChange) {1234setText((a != null && !hideText) ?1235(String)a.getValue(Action.NAME) : null);1236}1237else if (!hideText) {1238setText((String)a.getValue(Action.NAME));1239}1240}12411242void setIconFromAction(Action a) {1243Icon icon = null;1244if (a != null) {1245icon = (Icon)a.getValue(Action.LARGE_ICON_KEY);1246if (icon == null) {1247icon = (Icon)a.getValue(Action.SMALL_ICON);1248}1249}1250setIcon(icon);1251}12521253void smallIconChanged(Action a) {1254if (a.getValue(Action.LARGE_ICON_KEY) == null) {1255setIconFromAction(a);1256}1257}12581259void largeIconChanged(Action a) {1260setIconFromAction(a);1261}12621263private void setActionCommandFromAction(Action a) {1264setActionCommand((a != null) ?1265(String)a.getValue(Action.ACTION_COMMAND_KEY) :1266null);1267}12681269/**1270* Sets the seleted state of the button from the action. This is defined1271* here, but not wired up. Subclasses like JToggleButton and1272* JCheckBoxMenuItem make use of it.1273*1274* @param a the Action1275*/1276private void setSelectedFromAction(Action a) {1277boolean selected = false;1278if (a != null) {1279selected = AbstractAction.isSelected(a);1280}1281if (selected != isSelected()) {1282// This won't notify ActionListeners, but that should be1283// ok as the change is coming from the Action.1284setSelected(selected);1285// Make sure the change actually took effect1286if (!selected && isSelected()) {1287if (getModel() instanceof DefaultButtonModel) {1288ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup();1289if (group != null) {1290group.clearSelection();1291}1292}1293}1294}1295}12961297/**1298* Creates and returns a <code>PropertyChangeListener</code> that is1299* responsible for listening for changes from the specified1300* <code>Action</code> and updating the appropriate properties.1301* <p>1302* <b>Warning:</b> If you subclass this do not create an anonymous1303* inner class. If you do the lifetime of the button will be tied to1304* that of the <code>Action</code>.1305*1306* @param a the button's action1307* @return the {@code PropertyChangeListener}1308* @since 1.31309* @see Action1310* @see #setAction1311*/1312protected PropertyChangeListener createActionPropertyChangeListener(Action a) {1313return createActionPropertyChangeListener0(a);1314}131513161317PropertyChangeListener createActionPropertyChangeListener0(Action a) {1318return new ButtonActionPropertyChangeListener(this, a);1319}13201321@SuppressWarnings("serial")1322private static class ButtonActionPropertyChangeListener1323extends ActionPropertyChangeListener<AbstractButton> {1324ButtonActionPropertyChangeListener(AbstractButton b, Action a) {1325super(b, a);1326}1327protected void actionPropertyChanged(AbstractButton button,1328Action action,1329PropertyChangeEvent e) {1330if (AbstractAction.shouldReconfigure(e)) {1331button.configurePropertiesFromAction(action);1332} else {1333button.actionPropertyChanged(action, e.getPropertyName());1334}1335}1336}13371338/**1339* Gets the <code>borderPainted</code> property.1340*1341* @return the value of the <code>borderPainted</code> property1342* @see #setBorderPainted1343*/1344public boolean isBorderPainted() {1345return paintBorder;1346}13471348/**1349* Sets the <code>borderPainted</code> property.1350* If <code>true</code> and the button has a border,1351* the border is painted. The default value for the1352* <code>borderPainted</code> property is <code>true</code>.1353* <p>1354* Some look and feels might not support1355* the <code>borderPainted</code> property,1356* in which case they ignore this.1357*1358* @param b if true and border property is not <code>null</code>,1359* the border is painted1360* @see #isBorderPainted1361*/1362@BeanProperty(visualUpdate = true, description1363= "Whether the border should be painted.")1364public void setBorderPainted(boolean b) {1365boolean oldValue = paintBorder;1366paintBorder = b;1367borderPaintedSet = true;1368firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);1369if (b != oldValue) {1370revalidate();1371repaint();1372}1373}13741375/**1376* Paint the button's border if <code>BorderPainted</code>1377* property is true and the button has a border.1378* @param g the <code>Graphics</code> context in which to paint1379*1380* @see #paint1381* @see #setBorder1382*/1383protected void paintBorder(Graphics g) {1384if (isBorderPainted()) {1385super.paintBorder(g);1386}1387}13881389/**1390* Gets the <code>paintFocus</code> property.1391*1392* @return the <code>paintFocus</code> property1393* @see #setFocusPainted1394*/1395public boolean isFocusPainted() {1396return paintFocus;1397}13981399/**1400* Sets the <code>paintFocus</code> property, which must1401* be <code>true</code> for the focus state to be painted.1402* The default value for the <code>paintFocus</code> property1403* is <code>true</code>.1404* Some look and feels might not paint focus state;1405* they will ignore this property.1406*1407* @param b if <code>true</code>, the focus state should be painted1408* @see #isFocusPainted1409*/1410@BeanProperty(visualUpdate = true, description1411= "Whether focus should be painted")1412public void setFocusPainted(boolean b) {1413boolean oldValue = paintFocus;1414paintFocus = b;1415firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);1416if (b != oldValue && isFocusOwner()) {1417revalidate();1418repaint();1419}1420}14211422/**1423* Gets the <code>contentAreaFilled</code> property.1424*1425* @return the <code>contentAreaFilled</code> property1426* @see #setContentAreaFilled1427*/1428public boolean isContentAreaFilled() {1429return contentAreaFilled;1430}14311432/**1433* Sets the <code>contentAreaFilled</code> property.1434* If <code>true</code> the button will paint the content1435* area. If you wish to have a transparent button, such as1436* an icon only button, for example, then you should set1437* this to <code>false</code>. Do not call <code>setOpaque(false)</code>.1438* The default value for the <code>contentAreaFilled</code>1439* property is <code>true</code>.1440* <p>1441* This function may cause the component's opaque property to change.1442* <p>1443* The exact behavior of calling this function varies on a1444* component-by-component and L&F-by-L&F basis.1445*1446* @param b if true, the content should be filled; if false1447* the content area is not filled1448* @see #isContentAreaFilled1449* @see #setOpaque1450*/1451@BeanProperty(visualUpdate = true, description1452= "Whether the button should paint the content area or leave it transparent.")1453public void setContentAreaFilled(boolean b) {1454boolean oldValue = contentAreaFilled;1455contentAreaFilled = b;1456contentAreaFilledSet = true;1457firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);1458if (b != oldValue) {1459repaint();1460}1461}14621463/**1464* Gets the <code>rolloverEnabled</code> property.1465*1466* @return the value of the <code>rolloverEnabled</code> property1467* @see #setRolloverEnabled1468*/1469public boolean isRolloverEnabled() {1470return rolloverEnabled;1471}14721473/**1474* Sets the <code>rolloverEnabled</code> property, which1475* must be <code>true</code> for rollover effects to occur.1476* The default value for the <code>rolloverEnabled</code>1477* property is <code>false</code>.1478* Some look and feels might not implement rollover effects;1479* they will ignore this property.1480*1481* @param b if <code>true</code>, rollover effects should be painted1482* @see #isRolloverEnabled1483*/1484@BeanProperty(visualUpdate = true, description1485= "Whether rollover effects should be enabled.")1486public void setRolloverEnabled(boolean b) {1487boolean oldValue = rolloverEnabled;1488rolloverEnabled = b;1489rolloverEnabledSet = true;1490firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);1491if (b != oldValue) {1492repaint();1493}1494}14951496/**1497* Returns the keyboard mnemonic from the current model.1498* @return the keyboard mnemonic from the model1499*/1500public int getMnemonic() {1501return mnemonic;1502}15031504/**1505* Sets the keyboard mnemonic on the current model.1506* The mnemonic is the key which when combined with the look and feel's1507* mouseless modifier (usually Alt) will activate this button1508* if focus is contained somewhere within this button's ancestor1509* window.1510* <p>1511* A mnemonic must correspond to a single key on the keyboard1512* and should be specified using one of the <code>VK_XXX</code>1513* keycodes defined in <code>java.awt.event.KeyEvent</code>.1514* These codes and the wider array of codes for international1515* keyboards may be obtained through1516* <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>.1517* Mnemonics are case-insensitive, therefore a key event1518* with the corresponding keycode would cause the button to be1519* activated whether or not the Shift modifier was pressed.1520* <p>1521* If the character defined by the mnemonic is found within1522* the button's label string, the first occurrence of it1523* will be underlined to indicate the mnemonic to the user.1524*1525* @param mnemonic the key code which represents the mnemonic1526* @see java.awt.event.KeyEvent1527* @see #setDisplayedMnemonicIndex1528*/1529@BeanProperty(visualUpdate = true, description1530= "the keyboard character mnemonic")1531public void setMnemonic(int mnemonic) {1532int oldValue = getMnemonic();1533model.setMnemonic(mnemonic);1534updateMnemonicProperties();1535}15361537/**1538* This method is now obsolete, please use <code>setMnemonic(int)</code>1539* to set the mnemonic for a button. This method is only designed1540* to handle character values which fall between 'a' and 'z' or1541* 'A' and 'Z'.1542*1543* @param mnemonic a char specifying the mnemonic value1544* @see #setMnemonic(int)1545*/1546@BeanProperty(visualUpdate = true, description1547= "the keyboard character mnemonic")1548public void setMnemonic(char mnemonic) {1549int vk = (int) mnemonic;1550if(vk >= 'a' && vk <='z')1551vk -= ('a' - 'A');1552setMnemonic(vk);1553}15541555/**1556* Provides a hint to the look and feel as to which character in the1557* text should be decorated to represent the mnemonic. Not all look and1558* feels may support this. A value of -1 indicates either there is no1559* mnemonic, the mnemonic character is not contained in the string, or1560* the developer does not wish the mnemonic to be displayed.1561* <p>1562* The value of this is updated as the properties relating to the1563* mnemonic change (such as the mnemonic itself, the text...).1564* You should only ever have to call this if1565* you do not wish the default character to be underlined. For example, if1566* the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'1567* to be decorated, as 'Save <u>A</u>s', you would have to invoke1568* <code>setDisplayedMnemonicIndex(5)</code> after invoking1569* <code>setMnemonic(KeyEvent.VK_A)</code>.1570*1571* @since 1.41572* @param index Index into the String to underline1573* @exception IllegalArgumentException will be thrown if <code>index</code>1574* is >= length of the text, or < -11575* @see #getDisplayedMnemonicIndex1576*/1577@BeanProperty(visualUpdate = true, description1578= "the index into the String to draw the keyboard character mnemonic at")1579public void setDisplayedMnemonicIndex(int index)1580throws IllegalArgumentException {1581int oldValue = mnemonicIndex;1582if (index == -1) {1583mnemonicIndex = -1;1584} else {1585String text = getText();1586int textLength = (text == null) ? 0 : text.length();1587if (index < -1 || index >= textLength) { // index out of range1588throw new IllegalArgumentException("index == " + index);1589}1590}1591mnemonicIndex = index;1592firePropertyChange("displayedMnemonicIndex", oldValue, index);1593if (index != oldValue) {1594revalidate();1595repaint();1596}1597}15981599/**1600* Returns the character, as an index, that the look and feel should1601* provide decoration for as representing the mnemonic character.1602*1603* @since 1.41604* @return index representing mnemonic character1605* @see #setDisplayedMnemonicIndex1606*/1607public int getDisplayedMnemonicIndex() {1608return mnemonicIndex;1609}16101611/**1612* Update the displayedMnemonicIndex property. This method1613* is called when either text or mnemonic changes. The new1614* value of the displayedMnemonicIndex property is the index1615* of the first occurrence of mnemonic in text.1616*/1617private void updateDisplayedMnemonicIndex(String text, int mnemonic) {1618setDisplayedMnemonicIndex(1619SwingUtilities.findDisplayedMnemonicIndex(text, mnemonic));1620}16211622/**1623* Brings the mnemonic property in accordance with model's mnemonic.1624* This is called when model's mnemonic changes. Also updates the1625* displayedMnemonicIndex property.1626*/1627private void updateMnemonicProperties() {1628int newMnemonic = model.getMnemonic();1629if (mnemonic != newMnemonic) {1630int oldValue = mnemonic;1631mnemonic = newMnemonic;1632firePropertyChange(MNEMONIC_CHANGED_PROPERTY,1633oldValue, mnemonic);1634updateDisplayedMnemonicIndex(getText(), mnemonic);1635revalidate();1636repaint();1637}1638}16391640/**1641* Sets the amount of time (in milliseconds) required between1642* mouse press events for the button to generate the corresponding1643* action events. After the initial mouse press occurs (and action1644* event generated) any subsequent mouse press events which occur1645* on intervals less than the threshhold will be ignored and no1646* corresponding action event generated. By default the threshhold is 0,1647* which means that for each mouse press, an action event will be1648* fired, no matter how quickly the mouse clicks occur. In buttons1649* where this behavior is not desirable (for example, the "OK" button1650* in a dialog), this threshhold should be set to an appropriate1651* positive value.1652*1653* @see #getMultiClickThreshhold1654* @param threshhold the amount of time required between mouse1655* press events to generate corresponding action events1656* @exception IllegalArgumentException if threshhold < 01657* @since 1.41658*/1659public void setMultiClickThreshhold(long threshhold) {1660if (threshhold < 0) {1661throw new IllegalArgumentException("threshhold must be >= 0");1662}1663this.multiClickThreshhold = threshhold;1664}16651666/**1667* Gets the amount of time (in milliseconds) required between1668* mouse press events for the button to generate the corresponding1669* action events.1670*1671* @see #setMultiClickThreshhold1672* @return the amount of time required between mouse press events1673* to generate corresponding action events1674* @since 1.41675*/1676public long getMultiClickThreshhold() {1677return multiClickThreshhold;1678}16791680/**1681* Returns the model that this button represents.1682* @return the <code>model</code> property1683* @see #setModel1684*/1685public ButtonModel getModel() {1686return model;1687}16881689/**1690* Sets the model that this button represents.1691* @param newModel the new <code>ButtonModel</code>1692* @see #getModel1693*/1694@BeanProperty(description1695= "Model that the Button uses.")1696public void setModel(ButtonModel newModel) {16971698ButtonModel oldModel = getModel();16991700if (oldModel != null) {1701oldModel.removeChangeListener(changeListener);1702oldModel.removeActionListener(actionListener);1703oldModel.removeItemListener(itemListener);1704changeListener = null;1705actionListener = null;1706itemListener = null;1707}17081709model = newModel;17101711if (newModel != null) {1712changeListener = createChangeListener();1713actionListener = createActionListener();1714itemListener = createItemListener();1715newModel.addChangeListener(changeListener);1716newModel.addActionListener(actionListener);1717newModel.addItemListener(itemListener);17181719updateMnemonicProperties();1720//We invoke setEnabled() from JComponent1721//because setModel() can be called from a constructor1722//when the button is not fully initialized1723super.setEnabled(newModel.isEnabled());17241725} else {1726mnemonic = '\0';1727}17281729updateDisplayedMnemonicIndex(getText(), mnemonic);17301731firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);1732if (newModel != oldModel) {1733revalidate();1734repaint();1735}1736}173717381739/**1740* Returns the L&F object that renders this component.1741* @return the ButtonUI object1742* @see #setUI1743*/1744public ButtonUI getUI() {1745return (ButtonUI) ui;1746}174717481749/**1750* Sets the L&F object that renders this component.1751* @param ui the <code>ButtonUI</code> L&F object1752* @see #getUI1753*/1754@BeanProperty(hidden = true, visualUpdate = true, description1755= "The UI object that implements the LookAndFeel.")1756public void setUI(ButtonUI ui) {1757super.setUI(ui);1758// disabled icons are generated by the LF so they should be unset here1759if (disabledIcon instanceof UIResource) {1760setDisabledIcon(null);1761}1762if (disabledSelectedIcon instanceof UIResource) {1763setDisabledSelectedIcon(null);1764}1765}176617671768/**1769* Resets the UI property to a value from the current look1770* and feel. Subtypes of <code>AbstractButton</code>1771* should override this to update the UI. For1772* example, <code>JButton</code> might do the following:1773* <pre>1774* setUI((ButtonUI)UIManager.getUI(1775* "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));1776* </pre>1777*/1778public void updateUI() {1779}17801781/**1782* Adds the specified component to this container at the specified1783* index, refer to1784* {@link java.awt.Container#addImpl(Component, Object, int)}1785* for a complete description of this method.1786*1787* @param comp the component to be added1788* @param constraints an object expressing layout constraints1789* for this component1790* @param index the position in the container's list at which to1791* insert the component, where <code>-1</code>1792* means append to the end1793* @exception IllegalArgumentException if <code>index</code> is invalid1794* @exception IllegalArgumentException if adding the container's parent1795* to itself1796* @exception IllegalArgumentException if adding a window to a container1797* @since 1.51798*/1799protected void addImpl(Component comp, Object constraints, int index) {1800if (!setLayout) {1801setLayout(new OverlayLayout(this));1802}1803super.addImpl(comp, constraints, index);1804}18051806/**1807* Sets the layout manager for this container, refer to1808* {@link java.awt.Container#setLayout(LayoutManager)}1809* for a complete description of this method.1810*1811* @param mgr the specified layout manager1812* @since 1.51813*/1814public void setLayout(LayoutManager mgr) {1815setLayout = true;1816super.setLayout(mgr);1817}18181819/**1820* Adds a <code>ChangeListener</code> to the button.1821* @param l the listener to be added1822*/1823public void addChangeListener(ChangeListener l) {1824listenerList.add(ChangeListener.class, l);1825}18261827/**1828* Removes a ChangeListener from the button.1829* @param l the listener to be removed1830*/1831public void removeChangeListener(ChangeListener l) {1832listenerList.remove(ChangeListener.class, l);1833}18341835/**1836* Returns an array of all the <code>ChangeListener</code>s added1837* to this AbstractButton with addChangeListener().1838*1839* @return all of the <code>ChangeListener</code>s added or an empty1840* array if no listeners have been added1841* @since 1.41842*/1843@BeanProperty(bound = false)1844public ChangeListener[] getChangeListeners() {1845return listenerList.getListeners(ChangeListener.class);1846}18471848/**1849* Notifies all listeners that have registered interest for1850* notification on this event type. The event instance1851* is lazily created.1852* @see EventListenerList1853*/1854protected void fireStateChanged() {1855// Guaranteed to return a non-null array1856Object[] listeners = listenerList.getListenerList();1857// Process the listeners last to first, notifying1858// those that are interested in this event1859for (int i = listeners.length-2; i>=0; i-=2) {1860if (listeners[i]==ChangeListener.class) {1861// Lazily create the event:1862if (changeEvent == null)1863changeEvent = new ChangeEvent(this);1864((ChangeListener)listeners[i+1]).stateChanged(changeEvent);1865}1866}1867}18681869/**1870* Adds an <code>ActionListener</code> to the button.1871* @param l the <code>ActionListener</code> to be added1872*/1873public void addActionListener(ActionListener l) {1874listenerList.add(ActionListener.class, l);1875}18761877/**1878* Removes an <code>ActionListener</code> from the button.1879* If the listener is the currently set <code>Action</code>1880* for the button, then the <code>Action</code>1881* is set to <code>null</code>.1882*1883* @param l the listener to be removed1884*/1885public void removeActionListener(ActionListener l) {1886if ((l != null) && (getAction() == l)) {1887setAction(null);1888} else {1889listenerList.remove(ActionListener.class, l);1890}1891}18921893/**1894* Returns an array of all the <code>ActionListener</code>s added1895* to this AbstractButton with addActionListener().1896*1897* @return all of the <code>ActionListener</code>s added or an empty1898* array if no listeners have been added1899* @since 1.41900*/1901@BeanProperty(bound = false)1902public ActionListener[] getActionListeners() {1903return listenerList.getListeners(ActionListener.class);1904}19051906/**1907* Subclasses that want to handle <code>ChangeEvents</code> differently1908* can override this to return another <code>ChangeListener</code>1909* implementation.1910*1911* @return the new <code>ChangeListener</code>1912*/1913protected ChangeListener createChangeListener() {1914return getHandler();1915}19161917/**1918* Extends <code>ChangeListener</code> to be serializable.1919* <p>1920* <strong>Warning:</strong>1921* Serialized objects of this class will not be compatible with1922* future Swing releases. The current serialization support is1923* appropriate for short term storage or RMI between applications running1924* the same version of Swing. As of 1.4, support for long term storage1925* of all JavaBeans1926* has been added to the <code>java.beans</code> package.1927* Please see {@link java.beans.XMLEncoder}.1928*/1929@SuppressWarnings("serial")1930protected class ButtonChangeListener implements ChangeListener, Serializable {1931// NOTE: This class is NOT used, instead the functionality has1932// been moved to Handler.1933ButtonChangeListener() {1934}19351936public void stateChanged(ChangeEvent e) {1937getHandler().stateChanged(e);1938}1939}194019411942/**1943* Notifies all listeners that have registered interest for1944* notification on this event type. The event instance1945* is lazily created using the <code>event</code>1946* parameter.1947*1948* @param event the <code>ActionEvent</code> object1949* @see EventListenerList1950*/1951protected void fireActionPerformed(ActionEvent event) {1952// Guaranteed to return a non-null array1953Object[] listeners = listenerList.getListenerList();1954ActionEvent e = null;1955// Process the listeners last to first, notifying1956// those that are interested in this event1957for (int i = listeners.length-2; i>=0; i-=2) {1958if (listeners[i]==ActionListener.class) {1959// Lazily create the event:1960if (e == null) {1961String actionCommand = event.getActionCommand();1962if(actionCommand == null) {1963actionCommand = getActionCommand();1964}1965e = new ActionEvent(AbstractButton.this,1966ActionEvent.ACTION_PERFORMED,1967actionCommand,1968event.getWhen(),1969event.getModifiers());1970}1971((ActionListener)listeners[i+1]).actionPerformed(e);1972}1973}1974}19751976/**1977* Notifies all listeners that have registered interest for1978* notification on this event type. The event instance1979* is lazily created using the <code>event</code> parameter.1980*1981* @param event the <code>ItemEvent</code> object1982* @see EventListenerList1983*/1984protected void fireItemStateChanged(ItemEvent event) {1985// Guaranteed to return a non-null array1986Object[] listeners = listenerList.getListenerList();1987ItemEvent e = null;1988// Process the listeners last to first, notifying1989// those that are interested in this event1990for (int i = listeners.length-2; i>=0; i-=2) {1991if (listeners[i]==ItemListener.class) {1992// Lazily create the event:1993if (e == null) {1994e = new ItemEvent(AbstractButton.this,1995ItemEvent.ITEM_STATE_CHANGED,1996AbstractButton.this,1997event.getStateChange());1998}1999((ItemListener)listeners[i+1]).itemStateChanged(e);2000}2001}2002if (accessibleContext != null) {2003if (event.getStateChange() == ItemEvent.SELECTED) {2004accessibleContext.firePropertyChange(2005AccessibleContext.ACCESSIBLE_STATE_PROPERTY,2006null, AccessibleState.SELECTED);2007accessibleContext.firePropertyChange(2008AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,2009Integer.valueOf(0), Integer.valueOf(1));2010} else {2011accessibleContext.firePropertyChange(2012AccessibleContext.ACCESSIBLE_STATE_PROPERTY,2013AccessibleState.SELECTED, null);2014accessibleContext.firePropertyChange(2015AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,2016Integer.valueOf(1), Integer.valueOf(0));2017}2018}2019}20202021/**2022* Returns {@code ActionListener} that is added to model.2023*2024* @return the {@code ActionListener}2025*/2026protected ActionListener createActionListener() {2027return getHandler();2028}20292030/**2031* Returns {@code ItemListener} that is added to model.2032*2033* @return the {@code ItemListener}2034*/2035protected ItemListener createItemListener() {2036return getHandler();2037}203820392040/**2041* Enables (or disables) the button.2042* @param b true to enable the button, otherwise false2043*/2044public void setEnabled(boolean b) {2045if (!b && model.isRollover()) {2046model.setRollover(false);2047}2048super.setEnabled(b);2049model.setEnabled(b);2050}20512052// *** Deprecated java.awt.Button APIs below *** //20532054/**2055* Returns the label text.2056*2057* @return a <code>String</code> containing the label2058* @deprecated - Replaced by <code>getText</code>2059*/2060@Deprecated2061public String getLabel() {2062return getText();2063}20642065/**2066* Sets the label text.2067*2068* @param label a <code>String</code> containing the text2069* @deprecated - Replaced by <code>setText(text)</code>2070*/2071@Deprecated2072@BeanProperty(description2073= "Replace by setText(text)")2074public void setLabel(String label) {2075setText(label);2076}20772078/**2079* Adds an <code>ItemListener</code> to the <code>checkbox</code>.2080* @param l the <code>ItemListener</code> to be added2081*/2082public void addItemListener(ItemListener l) {2083listenerList.add(ItemListener.class, l);2084}20852086/**2087* Removes an <code>ItemListener</code> from the button.2088* @param l the <code>ItemListener</code> to be removed2089*/2090public void removeItemListener(ItemListener l) {2091listenerList.remove(ItemListener.class, l);2092}20932094/**2095* Returns an array of all the <code>ItemListener</code>s added2096* to this AbstractButton with addItemListener().2097*2098* @return all of the <code>ItemListener</code>s added or an empty2099* array if no listeners have been added2100* @since 1.42101*/2102@BeanProperty(bound = false)2103public ItemListener[] getItemListeners() {2104return listenerList.getListeners(ItemListener.class);2105}21062107/**2108* Returns an array (length 1) containing the label or2109* <code>null</code> if the button is not selected.2110*2111* @return an array containing 1 Object: the text of the button,2112* if the item is selected; otherwise <code>null</code>2113*/2114@BeanProperty(bound = false)2115public Object[] getSelectedObjects() {2116if (isSelected() == false) {2117return null;2118}2119Object[] selectedObjects = new Object[1];2120selectedObjects[0] = getText();2121return selectedObjects;2122}21232124/**2125* Initialization of the {@code AbstractButton}.2126*2127* @param text the text of the button2128* @param icon the Icon image to display on the button2129*/2130protected void init(String text, Icon icon) {2131if(text != null) {2132setText(text);2133}21342135if(icon != null) {2136setIcon(icon);2137}21382139// Set the UI2140updateUI();21412142setAlignmentX(LEFT_ALIGNMENT);2143setAlignmentY(CENTER_ALIGNMENT);2144}214521462147/**2148* This is overridden to return false if the current <code>Icon</code>'s2149* <code>Image</code> is not equal to the2150* passed in <code>Image</code> <code>img</code>.2151*2152* @param img the <code>Image</code> to be compared2153* @param infoflags flags used to repaint the button when the image2154* is updated and which determine how much is to be painted2155* @param x the x coordinate2156* @param y the y coordinate2157* @param w the width2158* @param h the height2159* @see java.awt.image.ImageObserver2160* @see java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)2161*/2162public boolean imageUpdate(Image img, int infoflags,2163int x, int y, int w, int h) {2164Icon iconDisplayed = null;21652166if (!model.isEnabled()) {2167if (model.isSelected()) {2168iconDisplayed = getDisabledSelectedIcon();2169} else {2170iconDisplayed = getDisabledIcon();2171}2172} else if (model.isPressed() && model.isArmed()) {2173iconDisplayed = getPressedIcon();2174} else if (isRolloverEnabled() && model.isRollover()) {2175if (model.isSelected()) {2176iconDisplayed = getRolloverSelectedIcon();2177} else {2178iconDisplayed = getRolloverIcon();2179}2180} else if (model.isSelected()) {2181iconDisplayed = getSelectedIcon();2182}21832184if (iconDisplayed == null) {2185iconDisplayed = getIcon();2186}21872188if (iconDisplayed == null2189|| !SwingUtilities.doesIconReferenceImage(iconDisplayed, img)) {2190// We don't know about this image, disable the notification so2191// we don't keep repainting.2192return false;2193}2194return super.imageUpdate(img, infoflags, x, y, w, h);2195}21962197void setUIProperty(String propertyName, Object value) {2198if (propertyName == "borderPainted") {2199if (!borderPaintedSet) {2200setBorderPainted(((Boolean)value).booleanValue());2201borderPaintedSet = false;2202}2203} else if (propertyName == "rolloverEnabled") {2204if (!rolloverEnabledSet) {2205setRolloverEnabled(((Boolean)value).booleanValue());2206rolloverEnabledSet = false;2207}2208} else if (propertyName == "iconTextGap") {2209if (!iconTextGapSet) {2210setIconTextGap(((Number)value).intValue());2211iconTextGapSet = false;2212}2213} else if (propertyName == "contentAreaFilled") {2214if (!contentAreaFilledSet) {2215setContentAreaFilled(((Boolean)value).booleanValue());2216contentAreaFilledSet = false;2217}2218} else {2219super.setUIProperty(propertyName, value);2220}2221}22222223/**2224* Returns a string representation of this <code>AbstractButton</code>.2225* This method2226* is intended to be used only for debugging purposes, and the2227* content and format of the returned string may vary between2228* implementations. The returned string may be empty but may not2229* be <code>null</code>.2230* <P>2231* Overriding <code>paramString</code> to provide information about the2232* specific new aspects of the JFC components.2233*2234* @return a string representation of this <code>AbstractButton</code>2235*/2236protected String paramString() {2237String defaultIconString = ((defaultIcon != null)2238&& (defaultIcon != this) ?2239defaultIcon.toString() : "");2240String pressedIconString = ((pressedIcon != null)2241&& (pressedIcon != this) ?2242pressedIcon.toString() : "");2243String disabledIconString = ((disabledIcon != null)2244&& (disabledIcon != this) ?2245disabledIcon.toString() : "");2246String selectedIconString = ((selectedIcon != null)2247&& (selectedIcon != this) ?2248selectedIcon.toString() : "");2249String disabledSelectedIconString = ((disabledSelectedIcon != null) &&2250(disabledSelectedIcon != this) ?2251disabledSelectedIcon.toString()2252: "");2253String rolloverIconString = ((rolloverIcon != null)2254&& (rolloverIcon != this) ?2255rolloverIcon.toString() : "");2256String rolloverSelectedIconString = ((rolloverSelectedIcon != null) &&2257(rolloverSelectedIcon != this) ?2258rolloverSelectedIcon.toString()2259: "");2260String paintBorderString = (paintBorder ? "true" : "false");2261String paintFocusString = (paintFocus ? "true" : "false");2262String rolloverEnabledString = (rolloverEnabled ? "true" : "false");22632264return super.paramString() +2265",defaultIcon=" + defaultIconString +2266",disabledIcon=" + disabledIconString +2267",disabledSelectedIcon=" + disabledSelectedIconString +2268",margin=" + margin +2269",paintBorder=" + paintBorderString +2270",paintFocus=" + paintFocusString +2271",pressedIcon=" + pressedIconString +2272",rolloverEnabled=" + rolloverEnabledString +2273",rolloverIcon=" + rolloverIconString +2274",rolloverSelectedIcon=" + rolloverSelectedIconString +2275",selectedIcon=" + selectedIconString +2276",text=" + text;2277}227822792280private Handler getHandler() {2281if (handler == null) {2282handler = new Handler();2283}2284return handler;2285}228622872288//2289// Listeners that are added to model2290//2291@SuppressWarnings("serial")2292class Handler implements ActionListener, ChangeListener, ItemListener,2293Serializable {2294//2295// ChangeListener2296//2297public void stateChanged(ChangeEvent e) {2298Object source = e.getSource();22992300updateMnemonicProperties();2301if (isEnabled() != model.isEnabled()) {2302setEnabled(model.isEnabled());2303}2304fireStateChanged();2305repaint();2306}23072308//2309// ActionListener2310//2311public void actionPerformed(ActionEvent event) {2312fireActionPerformed(event);2313}23142315//2316// ItemListener2317//2318public void itemStateChanged(ItemEvent event) {2319fireItemStateChanged(event);2320if (shouldUpdateSelectedStateFromAction()) {2321Action action = getAction();2322if (action != null && AbstractAction.hasSelectedKey(action)) {2323boolean selected = isSelected();2324boolean isActionSelected = AbstractAction.isSelected(2325action);2326if (isActionSelected != selected) {2327action.putValue(Action.SELECTED_KEY, selected);2328}2329}2330}2331}2332}23332334///////////////////2335// Accessibility support2336///////////////////2337/**2338* This class implements accessibility support for the2339* <code>AbstractButton</code> class. It provides an implementation of the2340* Java Accessibility API appropriate to button and menu item2341* user-interface elements.2342* <p>2343* <strong>Warning:</strong>2344* Serialized objects of this class will not be compatible with2345* future Swing releases. The current serialization support is2346* appropriate for short term storage or RMI between applications running2347* the same version of Swing. As of 1.4, support for long term storage2348* of all JavaBeans2349* has been added to the <code>java.beans</code> package.2350* Please see {@link java.beans.XMLEncoder}.2351* @since 1.42352*/2353@SuppressWarnings("serial") // Same-version serialization only2354protected abstract class AccessibleAbstractButton2355extends AccessibleJComponent implements AccessibleAction,2356AccessibleValue, AccessibleText, AccessibleExtendedComponent {23572358/**2359* Constructor for subclasses to call.2360*/2361protected AccessibleAbstractButton() {}23622363/**2364* Returns the accessible name of this object.2365*2366* @return the localized name of the object -- can be2367* <code>null</code> if this2368* object does not have a name2369*/2370public String getAccessibleName() {2371String name = accessibleName;23722373if (name == null) {2374name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);2375}2376if (name == null) {2377name = AbstractButton.this.getText();2378}2379if (name == null) {2380name = super.getAccessibleName();2381}2382return name;2383}23842385/**2386* Get the AccessibleIcons associated with this object if one2387* or more exist. Otherwise return null.2388* @since 1.32389*/2390public AccessibleIcon [] getAccessibleIcon() {2391Icon defaultIcon = getIcon();23922393if (defaultIcon instanceof Accessible) {2394AccessibleContext ac =2395((Accessible)defaultIcon).getAccessibleContext();2396if (ac != null && ac instanceof AccessibleIcon) {2397return new AccessibleIcon[] { (AccessibleIcon)ac };2398}2399}2400return null;2401}24022403/**2404* Get the state set of this object.2405*2406* @return an instance of AccessibleState containing the current state2407* of the object2408* @see AccessibleState2409*/2410public AccessibleStateSet getAccessibleStateSet() {2411AccessibleStateSet states = super.getAccessibleStateSet();2412if (getModel().isArmed()) {2413states.add(AccessibleState.ARMED);2414}2415if (isFocusOwner()) {2416states.add(AccessibleState.FOCUSED);2417}2418if (getModel().isPressed()) {2419states.add(AccessibleState.PRESSED);2420}2421if (isSelected()) {2422states.add(AccessibleState.CHECKED);2423}2424return states;2425}24262427/**2428* Get the AccessibleRelationSet associated with this object if one2429* exists. Otherwise return null.2430* @see AccessibleRelation2431* @since 1.32432*/2433public AccessibleRelationSet getAccessibleRelationSet() {24342435// Check where the AccessibleContext's relation2436// set already contains a MEMBER_OF relation.2437AccessibleRelationSet relationSet2438= super.getAccessibleRelationSet();24392440if (!relationSet.contains(AccessibleRelation.MEMBER_OF)) {2441// get the members of the button group if one exists2442ButtonModel model = getModel();2443if (model != null && model instanceof DefaultButtonModel) {2444ButtonGroup group = ((DefaultButtonModel)model).getGroup();2445if (group != null) {2446// set the target of the MEMBER_OF relation to be2447// the members of the button group.2448int len = group.getButtonCount();2449Object [] target = new Object[len];2450Enumeration<AbstractButton> elem = group.getElements();2451for (int i = 0; i < len; i++) {2452if (elem.hasMoreElements()) {2453target[i] = elem.nextElement();2454}2455}2456AccessibleRelation relation =2457new AccessibleRelation(AccessibleRelation.MEMBER_OF);2458relation.setTarget(target);2459relationSet.add(relation);2460}2461}2462}2463return relationSet;2464}24652466/**2467* Get the AccessibleAction associated with this object. In the2468* implementation of the Java Accessibility API for this class,2469* return this object, which is responsible for implementing the2470* AccessibleAction interface on behalf of itself.2471*2472* @return this object2473*/2474public AccessibleAction getAccessibleAction() {2475return this;2476}24772478/**2479* Get the AccessibleValue associated with this object. In the2480* implementation of the Java Accessibility API for this class,2481* return this object, which is responsible for implementing the2482* AccessibleValue interface on behalf of itself.2483*2484* @return this object2485*/2486public AccessibleValue getAccessibleValue() {2487return this;2488}24892490/**2491* Returns the number of Actions available in this object. The2492* default behavior of a button is to have one action - toggle2493* the button.2494*2495* @return 1, the number of Actions in this object2496*/2497public int getAccessibleActionCount() {2498return 1;2499}25002501/**2502* Return a description of the specified action of the object.2503*2504* @param i zero-based index of the actions2505*/2506public String getAccessibleActionDescription(int i) {2507if (i == 0) {2508return UIManager.getString("AbstractButton.clickText");2509} else {2510return null;2511}2512}25132514/**2515* Perform the specified Action on the object2516*2517* @param i zero-based index of actions2518* @return true if the action was performed; else false.2519*/2520public boolean doAccessibleAction(int i) {2521if (i == 0) {2522doClick();2523return true;2524} else {2525return false;2526}2527}25282529/**2530* Get the value of this object as a Number.2531*2532* @return An Integer of 0 if this isn't selected or an Integer of 1 if2533* this is selected.2534* @see AbstractButton#isSelected2535*/2536public Number getCurrentAccessibleValue() {2537if (isSelected()) {2538return Integer.valueOf(1);2539} else {2540return Integer.valueOf(0);2541}2542}25432544/**2545* Set the value of this object as a Number.2546*2547* @return True if the value was set.2548*/2549public boolean setCurrentAccessibleValue(Number n) {2550// TIGER - 44225352551if (n == null) {2552return false;2553}2554int i = n.intValue();2555if (i == 0) {2556setSelected(false);2557} else {2558setSelected(true);2559}2560return true;2561}25622563/**2564* Get the minimum value of this object as a Number.2565*2566* @return an Integer of 0.2567*/2568public Number getMinimumAccessibleValue() {2569return Integer.valueOf(0);2570}25712572/**2573* Get the maximum value of this object as a Number.2574*2575* @return An Integer of 1.2576*/2577public Number getMaximumAccessibleValue() {2578return Integer.valueOf(1);2579}258025812582/* AccessibleText ---------- */25832584public AccessibleText getAccessibleText() {2585View view = (View)AbstractButton.this.getClientProperty("html");2586if (view != null) {2587return this;2588} else {2589return null;2590}2591}25922593/**2594* Given a point in local coordinates, return the zero-based index2595* of the character under that Point. If the point is invalid,2596* this method returns -1.2597*2598* Note: the AbstractButton must have a valid size (e.g. have2599* been added to a parent container whose ancestor container2600* is a valid top-level window) for this method to be able2601* to return a meaningful value.2602*2603* @param p the Point in local coordinates2604* @return the zero-based index of the character under Point p; if2605* Point is invalid returns -1.2606* @since 1.32607*/2608public int getIndexAtPoint(Point p) {2609View view = (View) AbstractButton.this.getClientProperty("html");2610if (view != null) {2611Rectangle r = getTextRectangle();2612if (r == null) {2613return -1;2614}2615Rectangle2D.Float shape =2616new Rectangle2D.Float(r.x, r.y, r.width, r.height);2617Position.Bias[] bias = new Position.Bias[1];2618return view.viewToModel(p.x, p.y, shape, bias);2619} else {2620return -1;2621}2622}26232624/**2625* Determine the bounding box of the character at the given2626* index into the string. The bounds are returned in local2627* coordinates. If the index is invalid an empty rectangle is2628* returned.2629*2630* Note: the AbstractButton must have a valid size (e.g. have2631* been added to a parent container whose ancestor container2632* is a valid top-level window) for this method to be able2633* to return a meaningful value.2634*2635* @param i the index into the String2636* @return the screen coordinates of the character's the bounding box,2637* if index is invalid returns an empty rectangle.2638* @since 1.32639*/2640public Rectangle getCharacterBounds(int i) {2641View view = (View) AbstractButton.this.getClientProperty("html");2642if (view != null) {2643Rectangle r = getTextRectangle();2644if (r == null) {2645return null;2646}2647Rectangle2D.Float shape =2648new Rectangle2D.Float(r.x, r.y, r.width, r.height);2649try {2650Shape charShape =2651view.modelToView(i, shape, Position.Bias.Forward);2652return charShape.getBounds();2653} catch (BadLocationException e) {2654return null;2655}2656} else {2657return null;2658}2659}26602661/**2662* Return the number of characters (valid indicies)2663*2664* @return the number of characters2665* @since 1.32666*/2667public int getCharCount() {2668View view = (View) AbstractButton.this.getClientProperty("html");2669if (view != null) {2670Document d = view.getDocument();2671if (d instanceof StyledDocument) {2672StyledDocument doc = (StyledDocument)d;2673return doc.getLength();2674}2675}2676return accessibleContext.getAccessibleName().length();2677}26782679/**2680* Return the zero-based offset of the caret.2681*2682* Note: That to the right of the caret will have the same index2683* value as the offset (the caret is between two characters).2684* @return the zero-based offset of the caret.2685* @since 1.32686*/2687public int getCaretPosition() {2688// There is no caret.2689return -1;2690}26912692/**2693* Returns the String at a given index.2694*2695* @param part the AccessibleText.CHARACTER, AccessibleText.WORD,2696* or AccessibleText.SENTENCE to retrieve2697* @param index an index within the text >= 02698* @return the letter, word, or sentence,2699* null for an invalid index or part2700* @since 1.32701*/2702public String getAtIndex(int part, int index) {2703if (index < 0 || index >= getCharCount()) {2704return null;2705}2706switch (part) {2707case AccessibleText.CHARACTER:2708try {2709return getText(index, 1);2710} catch (BadLocationException e) {2711return null;2712}2713case AccessibleText.WORD:2714try {2715String s = getText(0, getCharCount());2716BreakIterator words = BreakIterator.getWordInstance(getLocale());2717words.setText(s);2718int end = words.following(index);2719return s.substring(words.previous(), end);2720} catch (BadLocationException e) {2721return null;2722}2723case AccessibleText.SENTENCE:2724try {2725String s = getText(0, getCharCount());2726BreakIterator sentence =2727BreakIterator.getSentenceInstance(getLocale());2728sentence.setText(s);2729int end = sentence.following(index);2730return s.substring(sentence.previous(), end);2731} catch (BadLocationException e) {2732return null;2733}2734default:2735return null;2736}2737}27382739/**2740* Returns the String after a given index.2741*2742* @param part the AccessibleText.CHARACTER, AccessibleText.WORD,2743* or AccessibleText.SENTENCE to retrieve2744* @param index an index within the text >= 02745* @return the letter, word, or sentence, null for an invalid2746* index or part2747* @since 1.32748*/2749public String getAfterIndex(int part, int index) {2750if (index < 0 || index >= getCharCount()) {2751return null;2752}2753switch (part) {2754case AccessibleText.CHARACTER:2755if (index+1 >= getCharCount()) {2756return null;2757}2758try {2759return getText(index+1, 1);2760} catch (BadLocationException e) {2761return null;2762}2763case AccessibleText.WORD:2764try {2765String s = getText(0, getCharCount());2766BreakIterator words = BreakIterator.getWordInstance(getLocale());2767words.setText(s);2768int start = words.following(index);2769if (start == BreakIterator.DONE || start >= s.length()) {2770return null;2771}2772int end = words.following(start);2773if (end == BreakIterator.DONE || end >= s.length()) {2774return null;2775}2776return s.substring(start, end);2777} catch (BadLocationException e) {2778return null;2779}2780case AccessibleText.SENTENCE:2781try {2782String s = getText(0, getCharCount());2783BreakIterator sentence =2784BreakIterator.getSentenceInstance(getLocale());2785sentence.setText(s);2786int start = sentence.following(index);2787if (start == BreakIterator.DONE || start > s.length()) {2788return null;2789}2790int end = sentence.following(start);2791if (end == BreakIterator.DONE || end > s.length()) {2792return null;2793}2794return s.substring(start, end);2795} catch (BadLocationException e) {2796return null;2797}2798default:2799return null;2800}2801}28022803/**2804* Returns the String before a given index.2805*2806* @param part the AccessibleText.CHARACTER, AccessibleText.WORD,2807* or AccessibleText.SENTENCE to retrieve2808* @param index an index within the text >= 02809* @return the letter, word, or sentence, null for an invalid index2810* or part2811* @since 1.32812*/2813public String getBeforeIndex(int part, int index) {2814if (index < 0 || index > getCharCount()-1) {2815return null;2816}2817switch (part) {2818case AccessibleText.CHARACTER:2819if (index == 0) {2820return null;2821}2822try {2823return getText(index-1, 1);2824} catch (BadLocationException e) {2825return null;2826}2827case AccessibleText.WORD:2828try {2829String s = getText(0, getCharCount());2830BreakIterator words = BreakIterator.getWordInstance(getLocale());2831words.setText(s);2832int end = words.following(index);2833end = words.previous();2834int start = words.previous();2835if (start == BreakIterator.DONE) {2836return null;2837}2838return s.substring(start, end);2839} catch (BadLocationException e) {2840return null;2841}2842case AccessibleText.SENTENCE:2843try {2844String s = getText(0, getCharCount());2845BreakIterator sentence =2846BreakIterator.getSentenceInstance(getLocale());2847sentence.setText(s);2848int end = sentence.following(index);2849end = sentence.previous();2850int start = sentence.previous();2851if (start == BreakIterator.DONE) {2852return null;2853}2854return s.substring(start, end);2855} catch (BadLocationException e) {2856return null;2857}2858default:2859return null;2860}2861}28622863/**2864* Return the AttributeSet for a given character at a given index2865*2866* @param i the zero-based index into the text2867* @return the AttributeSet of the character2868* @since 1.32869*/2870public AttributeSet getCharacterAttribute(int i) {2871View view = (View) AbstractButton.this.getClientProperty("html");2872if (view != null) {2873Document d = view.getDocument();2874if (d instanceof StyledDocument) {2875StyledDocument doc = (StyledDocument)d;2876Element elem = doc.getCharacterElement(i);2877if (elem != null) {2878return elem.getAttributes();2879}2880}2881}2882return null;2883}28842885/**2886* Returns the start offset within the selected text.2887* If there is no selection, but there is2888* a caret, the start and end offsets will be the same.2889*2890* @return the index into the text of the start of the selection2891* @since 1.32892*/2893public int getSelectionStart() {2894// Text cannot be selected.2895return -1;2896}28972898/**2899* Returns the end offset within the selected text.2900* If there is no selection, but there is2901* a caret, the start and end offsets will be the same.2902*2903* @return the index into the text of the end of the selection2904* @since 1.32905*/2906public int getSelectionEnd() {2907// Text cannot be selected.2908return -1;2909}29102911/**2912* Returns the portion of the text that is selected.2913*2914* @return the String portion of the text that is selected2915* @since 1.32916*/2917public String getSelectedText() {2918// Text cannot be selected.2919return null;2920}29212922/*2923* Returns the text substring starting at the specified2924* offset with the specified length.2925*/2926private String getText(int offset, int length)2927throws BadLocationException {29282929View view = (View) AbstractButton.this.getClientProperty("html");2930if (view != null) {2931Document d = view.getDocument();2932if (d instanceof StyledDocument) {2933StyledDocument doc = (StyledDocument)d;2934return doc.getText(offset, length);2935}2936}2937return null;2938}29392940/*2941* Returns the bounding rectangle for the component text.2942*/2943private Rectangle getTextRectangle() {29442945String text = AbstractButton.this.getText();2946Icon icon = (AbstractButton.this.isEnabled()) ? AbstractButton.this.getIcon() : AbstractButton.this.getDisabledIcon();29472948if ((icon == null) && (text == null)) {2949return null;2950}29512952Rectangle paintIconR = new Rectangle();2953Rectangle paintTextR = new Rectangle();2954Rectangle paintViewR = new Rectangle();2955Insets paintViewInsets = new Insets(0, 0, 0, 0);29562957paintViewInsets = AbstractButton.this.getInsets(paintViewInsets);2958paintViewR.x = paintViewInsets.left;2959paintViewR.y = paintViewInsets.top;2960paintViewR.width = AbstractButton.this.getWidth() - (paintViewInsets.left + paintViewInsets.right);2961paintViewR.height = AbstractButton.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);29622963String clippedText = SwingUtilities.layoutCompoundLabel(2964AbstractButton.this,2965getFontMetrics(getFont()),2966text,2967icon,2968AbstractButton.this.getVerticalAlignment(),2969AbstractButton.this.getHorizontalAlignment(),2970AbstractButton.this.getVerticalTextPosition(),2971AbstractButton.this.getHorizontalTextPosition(),2972paintViewR,2973paintIconR,2974paintTextR,29750);29762977return paintTextR;2978}29792980// ----- AccessibleExtendedComponent29812982/**2983* Returns the AccessibleExtendedComponent2984*2985* @return the AccessibleExtendedComponent2986*/2987AccessibleExtendedComponent getAccessibleExtendedComponent() {2988return this;2989}29902991/**2992* Returns the tool tip text2993*2994* @return the tool tip text, if supported, of the object;2995* otherwise, null2996* @since 1.42997*/2998public String getToolTipText() {2999return AbstractButton.this.getToolTipText();3000}30013002/**3003* Returns the titled border text3004*3005* @return the titled border text, if supported, of the object;3006* otherwise, null3007* @since 1.43008*/3009public String getTitledBorderText() {3010return super.getTitledBorderText();3011}30123013/**3014* Returns key bindings associated with this object3015*3016* @return the key bindings, if supported, of the object;3017* otherwise, null3018* @see AccessibleKeyBinding3019* @since 1.43020*/3021public AccessibleKeyBinding getAccessibleKeyBinding() {3022int mnemonic = AbstractButton.this.getMnemonic();3023if (mnemonic == 0) {3024return null;3025}3026return new ButtonKeyBinding(mnemonic);3027}30283029class ButtonKeyBinding implements AccessibleKeyBinding {3030int mnemonic;30313032ButtonKeyBinding(int mnemonic) {3033this.mnemonic = mnemonic;3034}30353036/**3037* Returns the number of key bindings for this object3038*3039* @return the zero-based number of key bindings for this object3040*/3041public int getAccessibleKeyBindingCount() {3042return 1;3043}30443045/**3046* Returns a key binding for this object. The value returned is an3047* java.lang.Object which must be cast to appropriate type depending3048* on the underlying implementation of the key. For example, if the3049* Object returned is a javax.swing.KeyStroke, the user of this3050* method should do the following:3051* <nf><code>3052* Component c = <get the component that has the key bindings>3053* AccessibleContext ac = c.getAccessibleContext();3054* AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();3055* for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {3056* Object o = akb.getAccessibleKeyBinding(i);3057* if (o instanceof javax.swing.KeyStroke) {3058* javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;3059* <do something with the key binding>3060* }3061* }3062* </code></nf>3063*3064* @param i zero-based index of the key bindings3065* @return a javax.lang.Object which specifies the key binding3066* @exception IllegalArgumentException if the index is3067* out of bounds3068* @see #getAccessibleKeyBindingCount3069*/3070public java.lang.Object getAccessibleKeyBinding(int i) {3071if (i != 0) {3072throw new IllegalArgumentException();3073}3074return KeyStroke.getKeyStroke(mnemonic, 0);3075}3076}3077}3078}307930803081