diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/javax/swing/AbstractButton.java | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/javax/swing/AbstractButton.java')
-rw-r--r-- | libjava/classpath/javax/swing/AbstractButton.java | 2652 |
1 files changed, 2652 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java new file mode 100644 index 000000000..6cbc7751f --- /dev/null +++ b/libjava/classpath/javax/swing/AbstractButton.java @@ -0,0 +1,2652 @@ +/* AbstractButton.java -- Provides basic button functionality. + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.ItemSelectable; +import java.awt.LayoutManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.image.ImageObserver; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.Serializable; +import java.util.Enumeration; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleIcon; +import javax.accessibility.AccessibleRelation; +import javax.accessibility.AccessibleRelationSet; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleText; +import javax.accessibility.AccessibleValue; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ButtonUI; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.Position; +import javax.swing.text.StyledDocument; +import javax.swing.text.View; + + +/** + * Provides an abstract implementation of common button behaviour, + * data model and look & feel. + * + * <p>This class is supposed to serve as a base class for + * several kinds of buttons with similar but non-identical semantics: + * toggle buttons (radio buttons and checkboxes), simple push buttons, + * menu items, etc.</p> + * + * <p>Buttons have many properties, some of which are stored in this class + * while others are delegated to the button's model. The following properties + * are available:</p> + * + * <table> + * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr> + * + * <tr><td>action </td><td>button</td> <td>no</td></tr> + * <tr><td>actionCommand </td><td>model</td> <td>no</td></tr> + * <tr><td>borderPainted </td><td>button</td> <td>yes</td></tr> + * <tr><td>contentAreaFilled </td><td>button</td> <td>yes</td></tr> + * <tr><td>disabledIcon </td><td>button</td> <td>yes</td></tr> + * <tr><td>disabledSelectedIcon </td><td>button</td> <td>yes</td></tr> + * <tr><td>displayedMnemonicIndex </td><td>button</td> <td>no</td></tr> + * <tr><td>enabled </td><td>model</td> <td>no</td></tr> + * <tr><td>focusPainted </td><td>button</td> <td>yes</td></tr> + * <tr><td>horizontalAlignment </td><td>button</td> <td>yes</td></tr> + * <tr><td>horizontalTextPosition </td><td>button</td> <td>yes</td></tr> + * <tr><td>icon </td><td>button</td> <td>yes</td></tr> + * <tr><td>iconTextGap </td><td>button</td> <td>no</td></tr> + * <tr><td>label (same as text) </td><td>model</td> <td>yes</td></tr> + * <tr><td>margin </td><td>button</td> <td>yes</td></tr> + * <tr><td>multiClickThreshold </td><td>button</td> <td>no</td></tr> + * <tr><td>pressedIcon </td><td>button</td> <td>yes</td></tr> + * <tr><td>rolloverEnabled </td><td>button</td> <td>yes</td></tr> + * <tr><td>rolloverIcon </td><td>button</td> <td>yes</td></tr> + * <tr><td>rolloverSelectedIcon </td><td>button</td> <td>yes</td></tr> + * <tr><td>selected </td><td>model</td> <td>no</td></tr> + * <tr><td>selectedIcon </td><td>button</td> <td>yes</td></tr> + * <tr><td>selectedObjects </td><td>button</td> <td>no</td></tr> + * <tr><td>text </td><td>model</td> <td>yes</td></tr> + * <tr><td>UI </td><td>button</td> <td>yes</td></tr> + * <tr><td>verticalAlignment </td><td>button</td> <td>yes</td></tr> + * <tr><td>verticalTextPosition </td><td>button</td> <td>yes</td></tr> + * + * </table> + * + * <p>The various behavioral aspects of these properties follows:</p> + * + * <ul> + * + * <li>When non-bound properties stored in the button change, the button + * fires ChangeEvents to its ChangeListeners.</li> + * + * <li>When bound properties stored in the button change, the button fires + * PropertyChangeEvents to its PropertyChangeListeners</li> + * + * <li>If any of the model's properties change, it fires a ChangeEvent to + * its ChangeListeners, which include the button.</li> + * + * <li>If the button receives a ChangeEvent from its model, it will + * propagate the ChangeEvent to its ChangeListeners, with the ChangeEvent's + * "source" property set to refer to the button, rather than the model. The + * the button will request a repaint, to paint its updated state.</li> + * + * <li>If the model's "selected" property changes, the model will fire an + * ItemEvent to its ItemListeners, which include the button, in addition to + * the ChangeEvent which models the property change. The button propagates + * ItemEvents directly to its ItemListeners.</li> + * + * <li>If the model's armed and pressed properties are simultaneously + * <code>true</code>, the model will fire an ActionEvent to its + * ActionListeners, which include the button. The button will propagate + * this ActionEvent to its ActionListeners, with the ActionEvent's "source" + * property set to refer to the button, rather than the model.</li> + * + * </ul> + * + * @author Ronald Veldema (rveldema@cs.vu.nl) + * @author Graydon Hoare (graydon@redhat.com) + */ + +public abstract class AbstractButton extends JComponent + implements ItemSelectable, SwingConstants +{ + private static final long serialVersionUID = -937921345538462020L; + + /** + * An extension of ChangeListener to be serializable. + */ + protected class ButtonChangeListener + implements ChangeListener, Serializable + { + private static final long serialVersionUID = 1471056094226600578L; + + /** + * The spec has no public/protected constructor for this class, so do we. + */ + ButtonChangeListener() + { + // Nothing to do here. + } + + /** + * Notified when the target of the listener changes its state. + * + * @param ev the ChangeEvent describing the change + */ + public void stateChanged(ChangeEvent ev) + { + getEventHandler().stateChanged(ev); + } + } + + /** + * The combined event handler for ActionEvent, ChangeEvent and + * ItemEvent. This combines ButtonChangeListener, ActionListener + */ + private class EventHandler + implements ActionListener, ChangeListener, ItemListener + { + public void actionPerformed(ActionEvent ev) + { + fireActionPerformed(ev); + } + + public void stateChanged(ChangeEvent ev) + { + fireStateChanged(); + repaint(); + } + + public void itemStateChanged(ItemEvent ev) + { + fireItemStateChanged(ev); + } + } + + /** The icon displayed by default. */ + Icon default_icon; + + /** The icon displayed when the button is pressed. */ + Icon pressed_icon; + + /** The icon displayed when the button is disabled. */ + Icon disabledIcon; + + /** The icon displayed when the button is selected. */ + Icon selectedIcon; + + /** The icon displayed when the button is selected but disabled. */ + Icon disabledSelectedIcon; + + /** The icon displayed when the button is rolled over. */ + Icon rolloverIcon; + + /** The icon displayed when the button is selected and rolled over. */ + Icon rolloverSelectedIcon; + + /** The icon currently displayed. */ + Icon current_icon; + + /** The text displayed in the button. */ + String text; + + /** + * The gap between icon and text, if both icon and text are + * non-<code>null</code>. + */ + int iconTextGap; + + /** The vertical alignment of the button's text and icon. */ + int verticalAlignment; + + /** The horizontal alignment of the button's text and icon. */ + int horizontalAlignment; + + /** The horizontal position of the button's text relative to its icon. */ + int horizontalTextPosition; + + /** The vertical position of the button's text relative to its icon. */ + int verticalTextPosition; + + /** Whether or not the button paints its border. */ + boolean borderPainted; + + /** Whether or not the button paints its focus state. */ + boolean focusPainted; + + /** Whether or not the button fills its content area. */ + boolean contentAreaFilled; + + /** Whether rollover is enabled. */ + boolean rollOverEnabled; + + /** The action taken when the button is clicked. */ + Action action; + + /** The button's current state. */ + protected ButtonModel model; + + /** The margin between the button's border and its label. */ + Insets margin; + + /** + * A hint to the look and feel class, suggesting which character in the + * button's label should be underlined when drawing the label. + */ + int mnemonicIndex; + + /** + * Listener the button uses to receive ActionEvents from its model. + */ + protected ActionListener actionListener; + + /** + * Listener the button uses to receive ItemEvents from its model. + */ + protected ItemListener itemListener; + + /** + * Listener the button uses to receive ChangeEvents from its model. + */ + protected ChangeListener changeListener; + + /** + * The event handler for ActionEvent, ItemEvent and ChangeEvent. + * This replaces the above three handlers and combines them + * into one for efficiency. + */ + private EventHandler eventHandler; + + /** + * The time in milliseconds in which clicks get coalesced into a single + * <code>ActionEvent</code>. + */ + long multiClickThreshhold; + + /** + * Listener the button uses to receive PropertyChangeEvents from its + * Action. + */ + PropertyChangeListener actionPropertyChangeListener; + + /** ChangeEvent that is fired to button's ChangeEventListeners */ + protected ChangeEvent changeEvent = new ChangeEvent(this); + + /** + * Indicates if the borderPainted property has been set by a client + * program or by the UI. + * + * @see #setUIProperty(String, Object) + * @see LookAndFeel#installProperty(JComponent, String, Object) + */ + private boolean clientBorderPaintedSet = false; + + /** + * Indicates if the rolloverEnabled property has been set by a client + * program or by the UI. + * + * @see #setUIProperty(String, Object) + * @see LookAndFeel#installProperty(JComponent, String, Object) + */ + private boolean clientRolloverEnabledSet = false; + + /** + * Indicates if the iconTextGap property has been set by a client + * program or by the UI. + * + * @see #setUIProperty(String, Object) + * @see LookAndFeel#installProperty(JComponent, String, Object) + */ + private boolean clientIconTextGapSet = false; + + /** + * Indicates if the contentAreaFilled property has been set by a client + * program or by the UI. + * + * @see #setUIProperty(String, Object) + * @see LookAndFeel#installProperty(JComponent, String, Object) + */ + private boolean clientContentAreaFilledSet = false; + + /** + * Fired in a PropertyChangeEvent when the "borderPainted" property changes. + */ + public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted"; + + /** + * Fired in a PropertyChangeEvent when the "contentAreaFilled" property + * changes. + */ + public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = + "contentAreaFilled"; + + /** + * Fired in a PropertyChangeEvent when the "disabledIcon" property changes. + */ + public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon"; + + /** + * Fired in a PropertyChangeEvent when the "disabledSelectedIcon" property + * changes. + */ + public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = + "disabledSelectedIcon"; + + /** + * Fired in a PropertyChangeEvent when the "focusPainted" property changes. + */ + public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted"; + + /** + * Fired in a PropertyChangeEvent when the "horizontalAlignment" property + * changes. + */ + public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = + "horizontalAlignment"; + + /** + * Fired in a PropertyChangeEvent when the "horizontalTextPosition" property + * changes. + */ + public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = + "horizontalTextPosition"; + + /** + * Fired in a PropertyChangeEvent when the "icon" property changes. */ + public static final String ICON_CHANGED_PROPERTY = "icon"; + + /** Fired in a PropertyChangeEvent when the "margin" property changes. */ + public static final String MARGIN_CHANGED_PROPERTY = "margin"; + + /** Fired in a PropertyChangeEvent when the "mnemonic" property changes. */ + public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic"; + + /** Fired in a PropertyChangeEvent when the "model" property changes. */ + public static final String MODEL_CHANGED_PROPERTY = "model"; + + /** Fired in a PropertyChangeEvent when the "pressedIcon" property changes. */ + public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon"; + + /** + * Fired in a PropertyChangeEvent when the "rolloverEnabled" property + * changes. + */ + public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = + "rolloverEnabled"; + + /** + * Fired in a PropertyChangeEvent when the "rolloverIcon" property changes. + */ + public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon"; + + /** + * Fired in a PropertyChangeEvent when the "rolloverSelectedIcon" property + * changes. + */ + public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = + "rolloverSelectedIcon"; + + /** + * Fired in a PropertyChangeEvent when the "selectedIcon" property changes. + */ + public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon"; + + /** Fired in a PropertyChangeEvent when the "text" property changes. */ + public static final String TEXT_CHANGED_PROPERTY = "text"; + + /** + * Fired in a PropertyChangeEvent when the "verticalAlignment" property + * changes. + */ + public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = + "verticalAlignment"; + + /** + * Fired in a PropertyChangeEvent when the "verticalTextPosition" property + * changes. + */ + public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = + "verticalTextPosition"; + + /** + * A Java Accessibility extension of the AbstractButton. + */ + protected abstract class AccessibleAbstractButton + extends AccessibleJComponent implements AccessibleAction, AccessibleValue, + AccessibleText + { + private static final long serialVersionUID = -5673062525319836790L; + + protected AccessibleAbstractButton() + { + // Nothing to do here yet. + } + + /** + * Returns the accessible state set of this object. In addition to the + * superclass's states, the <code>AccessibleAbstractButton</code> + * supports the following states: {@link AccessibleState#ARMED}, + * {@link AccessibleState#FOCUSED}, {@link AccessibleState#PRESSED} and + * {@link AccessibleState#CHECKED}. + * + * @return the current state of this accessible object + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet state = super.getAccessibleStateSet(); + + if (getModel().isArmed()) + state.add(AccessibleState.ARMED); + if (getModel().isPressed()) + state.add(AccessibleState.PRESSED); + if (isSelected()) + state.add(AccessibleState.CHECKED); + + return state; + } + + /** + * Returns the accessible name for the button. + */ + public String getAccessibleName() + { + String result = super.getAccessibleName(); + if (result == null) + result = text; + return result; + } + + /** + * Returns the accessible icons of this object. If the AbstractButton's + * icon is an Accessible, and it's AccessibleContext is an AccessibleIcon, + * then this AccessibleIcon is returned, otherwise <code>null</code>. + * + * @return the accessible icons of this object, or <code>null</code> if + * there is no accessible icon + */ + public AccessibleIcon[] getAccessibleIcon() + { + AccessibleIcon[] ret = null; + Icon icon = getIcon(); + if (icon instanceof Accessible) + { + AccessibleContext ctx = ((Accessible) icon).getAccessibleContext(); + if (ctx instanceof AccessibleIcon) + { + ret = new AccessibleIcon[]{ (AccessibleIcon) ctx }; + } + } + return ret; + } + + /** + * Returns the accessible relations of this AccessibleAbstractButton. + * If the AbstractButton is part of a ButtonGroup, then all the buttons + * in this button group are added as targets in a MEMBER_OF relation, + * otherwise an empty relation set is returned (from super). + * + * @return the accessible relations of this AccessibleAbstractButton + */ + public AccessibleRelationSet getAccessibleRelationSet() + { + AccessibleRelationSet relations = super.getAccessibleRelationSet(); + ButtonModel model = getModel(); + if (model instanceof DefaultButtonModel) + { + ButtonGroup group = ((DefaultButtonModel) model).getGroup(); + if (group != null) + { + Object[] target = new Object[group.getButtonCount()]; + Enumeration els = group.getElements(); + + for (int index = 0; els.hasMoreElements(); ++index) + { + target[index] = els.nextElement(); + } + + AccessibleRelation rel = + new AccessibleRelation(AccessibleRelation.MEMBER_OF); + rel.setTarget(target); + relations.add(rel); + } + } + return relations; + } + + /** + * Returns the accessible action associated with this object. For buttons, + * this will be <code>this</code>. + * + * @return <code>this</code> + */ + public AccessibleAction getAccessibleAction() + { + return this; + } + + /** + * Returns the accessible value of this AccessibleAbstractButton, which + * is always <code>this</code>. + * + * @return the accessible value of this AccessibleAbstractButton, which + * is always <code>this</code> + */ + public AccessibleValue getAccessibleValue() + { + return this; + } + + /** + * Returns the number of accessible actions that are supported by this + * object. Buttons support one action by default ('press button'), so this + * method always returns <code>1</code>. + * + * @return <code>1</code>, the number of supported accessible actions + */ + public int getAccessibleActionCount() + { + return 1; + } + + /** + * Returns a description for the action with the specified index or + * <code>null</code> if such action does not exist. + * + * @param actionIndex the zero based index to the actions + * + * @return a description for the action with the specified index or + * <code>null</code> if such action does not exist + */ + public String getAccessibleActionDescription(int actionIndex) + { + String descr = null; + if (actionIndex == 0) + { + // FIXME: Supply localized descriptions in the UIDefaults. + descr = UIManager.getString("AbstractButton.clickText"); + } + return descr; + } + + /** + * Performs the acccessible action with the specified index on this object. + * Since buttons have only one action by default (which is to press the + * button), this method performs a 'press button' when the specified index + * is <code>0</code> and nothing otherwise. + * + * @param actionIndex a zero based index into the actions of this button + * + * @return <code>true</code> if the specified action has been performed + * successfully, <code>false</code> otherwise + */ + public boolean doAccessibleAction(int actionIndex) + { + boolean retVal = false; + if (actionIndex == 0) + { + doClick(); + retVal = true; + } + return retVal; + } + + /** + * Returns the current value of this object as a number. This + * implementation returns an <code>Integer(1)</code> if the button is + * selected, <code>Integer(0)</code> if the button is not selected. + * + * @return the current value of this object as a number + */ + public Number getCurrentAccessibleValue() + { + Integer retVal; + if (isSelected()) + retVal = new Integer(1); + else + retVal = new Integer(0); + return retVal; + } + + /** + * Sets the current accessible value as object. If the specified number + * is 0 the button will be deselected, otherwise the button will + * be selected. + * + * @param value 0 for deselected button, other for selected button + * + * @return <code>true</code> if the value has been set, <code>false</code> + * otherwise + */ + public boolean setCurrentAccessibleValue(Number value) + { + boolean retVal = false; + if (value != null) + { + if (value.intValue() == 0) + setSelected(false); + else + setSelected(true); + retVal = true; + } + return retVal; + } + + /** + * Returns the minimum accessible value for the AccessibleAbstractButton, + * which is <code>0</code>. + * + * @return the minimimum accessible value for the AccessibleAbstractButton, + * which is <code>0</code> + */ + public Number getMinimumAccessibleValue() + { + return new Integer(0); + } + + /** + * Returns the maximum accessible value for the AccessibleAbstractButton, + * which is <code>1</code>. + * + * @return the maximum accessible value for the AccessibleAbstractButton, + * which is <code>1</code> + */ + public Number getMaximumAccessibleValue() + { + return new Integer(1); + } + + /** + * Returns the accessible text for this AccessibleAbstractButton. This + * will be <code>null</code> if the button has a non-HTML label, otherwise + * <code>this</code>. + * + * @return the accessible text for this AccessibleAbstractButton + */ + public AccessibleText getAccessibleText() + { + AccessibleText accessibleText = null; + if (getClientProperty(BasicHTML.propertyKey) != null) + accessibleText = this; + + return accessibleText; + } + + /** + * Returns the index of the label's character at the specified point, + * relative to the local bounds of the button. This only works for + * HTML labels. + * + * @param p the point, relative to the buttons local bounds + * + * @return the index of the label's character at the specified point + */ + public int getIndexAtPoint(Point p) + { + int index = -1; + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (view != null) + { + Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight()); + index = view.viewToModel(p.x, p.y, shape, new Position.Bias[1]); + } + return index; + } + + /** + * Returns the bounds of the character at the specified index of the + * button's label. This will only work for HTML labels. + * + * @param i the index of the character of the label + * + * @return the bounds of the character at the specified index of the + * button's label + */ + public Rectangle getCharacterBounds(int i) + { + Rectangle rect = null; + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (view != null) + { + Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight()); + try + { + Shape s = view.modelToView(i, shape, Position.Bias.Forward); + rect = s.getBounds(); + } + catch (BadLocationException ex) + { + rect = null; + } + } + return rect; + } + + /** + * Returns the number of characters in the button's label. + * + * @return the bounds of the character at the specified index of the + * button's label + */ + public int getCharCount() + { + int charCount; + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (view != null) + { + charCount = view.getDocument().getLength(); + } + else + { + charCount = getAccessibleName().length(); + } + return charCount; + } + + /** + * This always returns <code>-1</code> since there is no caret in a button. + * + * @return <code>-1</code> since there is no caret in a button + */ + public int getCaretPosition() + { + return -1; + } + + /** + * Returns the character, word or sentence at the specified index. The + * <code>part</code> parameter determines what is returned, the character, + * word or sentence after the index. + * + * @param part one of {@link AccessibleText#CHARACTER}, + * {@link AccessibleText#WORD} or + * {@link AccessibleText#SENTENCE}, specifying what is returned + * @param index the index + * + * @return the character, word or sentence after <code>index</code> + */ + public String getAtIndex(int part, int index) + { + String result = ""; + int startIndex = -1; + int endIndex = -1; + switch(part) + { + case AccessibleText.CHARACTER: + result = String.valueOf(text.charAt(index)); + break; + case AccessibleText.WORD: + startIndex = text.lastIndexOf(' ', index); + endIndex = text.indexOf(' ', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + case AccessibleText.SENTENCE: + default: + startIndex = text.lastIndexOf('.', index); + endIndex = text.indexOf('.', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + } + return result; + } + + /** + * Returns the character, word or sentence after the specified index. The + * <code>part</code> parameter determines what is returned, the character, + * word or sentence after the index. + * + * @param part one of {@link AccessibleText#CHARACTER}, + * {@link AccessibleText#WORD} or + * {@link AccessibleText#SENTENCE}, specifying what is returned + * @param index the index + * + * @return the character, word or sentence after <code>index</code> + */ + public String getAfterIndex(int part, int index) + { + String result = ""; + int startIndex = -1; + int endIndex = -1; + switch(part) + { + case AccessibleText.CHARACTER: + result = String.valueOf(text.charAt(index + 1)); + break; + case AccessibleText.WORD: + startIndex = text.indexOf(' ', index); + endIndex = text.indexOf(' ', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + case AccessibleText.SENTENCE: + default: + startIndex = text.indexOf('.', index); + endIndex = text.indexOf('.', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + } + return result; + } + + /** + * Returns the character, word or sentence before the specified index. The + * <code>part</code> parameter determines what is returned, the character, + * word or sentence before the index. + * + * @param part one of {@link AccessibleText#CHARACTER}, + * {@link AccessibleText#WORD} or + * {@link AccessibleText#SENTENCE}, specifying what is returned + * @param index the index + * + * @return the character, word or sentence before <code>index</code> + */ + public String getBeforeIndex(int part, int index) + { + String result = ""; + int startIndex = -1; + int endIndex = -1; + switch(part) + { + case AccessibleText.CHARACTER: + result = String.valueOf(text.charAt(index - 1)); + break; + case AccessibleText.WORD: + endIndex = text.lastIndexOf(' ', index); + if (endIndex == -1) + endIndex = 0; + startIndex = text.lastIndexOf(' ', endIndex - 1); + result = text.substring(startIndex + 1, endIndex); + break; + case AccessibleText.SENTENCE: + default: + endIndex = text.lastIndexOf('.', index); + if (endIndex == -1) + endIndex = 0; + startIndex = text.lastIndexOf('.', endIndex - 1); + result = text.substring(startIndex + 1, endIndex); + break; + } + return result; + } + + /** + * Returns the text attribute for the character at the specified character + * index. + * + * @param i the character index + * + * @return the character attributes for the specified character or + * <code>null</code> if the character has no attributes + */ + public AttributeSet getCharacterAttribute(int i) + { + AttributeSet atts = null; + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (view != null) + { + Document doc = view.getDocument(); + if (doc instanceof StyledDocument) + { + StyledDocument sDoc = (StyledDocument) doc; + Element charEl = sDoc.getCharacterElement(i); + if (charEl != null) + atts = charEl.getAttributes(); + } + } + return atts; + } + + /** + * This always returns <code>-1</code> since + * button labels can't be selected. + * + * @return <code>-1</code>, button labels can't be selected + */ + public int getSelectionStart() + { + return -1; + } + + /** + * This always returns <code>-1</code> since + * button labels can't be selected. + * + * @return <code>-1</code>, button labels can't be selected + */ + public int getSelectionEnd() + { + return -1; + } + + /** + * Returns the selected text. This always returns <code>null</code> since + * button labels can't be selected. + * + * @return <code>null</code>, button labels can't be selected + */ + public String getSelectedText() + { + return null; + } + } + + /** + * Creates a new AbstractButton object. Subclasses should call the following + * sequence in their constructor in order to initialize the button correctly: + * <pre> + * super(); + * init(text, icon); + * </pre> + * + * The {@link #init(String, Icon)} method is not called automatically by this + * constructor. + * + * @see #init(String, Icon) + */ + public AbstractButton() + { + horizontalAlignment = CENTER; + horizontalTextPosition = TRAILING; + verticalAlignment = CENTER; + verticalTextPosition = CENTER; + borderPainted = true; + contentAreaFilled = true; + focusPainted = true; + setFocusable(true); + setAlignmentX(CENTER_ALIGNMENT); + setAlignmentY(CENTER_ALIGNMENT); + setDisplayedMnemonicIndex(-1); + setOpaque(true); + text = ""; + // testing on JRE1.5 shows that the iconTextGap default value is + // hard-coded here and the 'Button.iconTextGap' setting in the + // UI defaults is ignored, at least by the MetalLookAndFeel + iconTextGap = 4; + } + + /** + * Get the model the button is currently using. + * + * @return The current model + */ + public ButtonModel getModel() + { + return model; + } + + /** + * Set the model the button is currently using. This un-registers all + * listeners associated with the current model, and re-registers them + * with the new model. + * + * @param newModel The new model + */ + public void setModel(ButtonModel newModel) + { + if (newModel == model) + return; + + if (model != null) + { + model.removeActionListener(actionListener); + actionListener = null; + model.removeChangeListener(changeListener); + changeListener = null; + model.removeItemListener(itemListener); + itemListener = null; + } + ButtonModel old = model; + model = newModel; + if (model != null) + { + actionListener = createActionListener(); + model.addActionListener(actionListener); + changeListener = createChangeListener(); + model.addChangeListener(changeListener); + itemListener = createItemListener(); + model.addItemListener(itemListener); + } + firePropertyChange(MODEL_CHANGED_PROPERTY, old, model); + revalidate(); + repaint(); + } + + protected void init(String text, Icon icon) + { + // If text is null, we fall back to the empty + // string (which is set using AbstractButton's + // constructor). + // This way the behavior of the JDK is matched. + if(text != null) + setText(text); + + if (icon != null) + default_icon = icon; + + updateUI(); + } + + /** + * <p>Returns the action command string for this button's model.</p> + * + * <p>If the action command was set to <code>null</code>, the button's + * text (label) is returned instead.</p> + * + * @return The current action command string from the button's model + */ + public String getActionCommand() + { + String ac = model.getActionCommand(); + if (ac != null) + return ac; + else + return text; + } + + /** + * Sets the action command string for this button's model. + * + * @param actionCommand The new action command string to set in the button's + * model. + */ + public void setActionCommand(String actionCommand) + { + if (model != null) + model.setActionCommand(actionCommand); + } + + /** + * Adds an ActionListener to the button's listener list. When the + * button's model is clicked it fires an ActionEvent, and these + * listeners will be called. + * + * @param l The new listener to add + */ + public void addActionListener(ActionListener l) + { + listenerList.add(ActionListener.class, l); + } + + /** + * Removes an ActionListener from the button's listener list. + * + * @param l The listener to remove + */ + public void removeActionListener(ActionListener l) + { + listenerList.remove(ActionListener.class, l); + } + + /** + * Returns all added <code>ActionListener</code> objects. + * + * @return an array of listeners + * + * @since 1.4 + */ + public ActionListener[] getActionListeners() + { + return (ActionListener[]) listenerList.getListeners(ActionListener.class); + } + + /** + * Adds an ItemListener to the button's listener list. When the button's + * model changes state (between any of ARMED, ENABLED, PRESSED, ROLLOVER + * or SELECTED) it fires an ItemEvent, and these listeners will be + * called. + * + * @param l The new listener to add + */ + public void addItemListener(ItemListener l) + { + listenerList.add(ItemListener.class, l); + } + + /** + * Removes an ItemListener from the button's listener list. + * + * @param l The listener to remove + */ + public void removeItemListener(ItemListener l) + { + listenerList.remove(ItemListener.class, l); + } + + /** + * Returns all added <code>ItemListener</code> objects. + * + * @return an array of listeners + * + * @since 1.4 + */ + public ItemListener[] getItemListeners() + { + return (ItemListener[]) listenerList.getListeners(ItemListener.class); + } + + /** + * Adds a ChangeListener to the button's listener list. When the button's + * model changes any of its (non-bound) properties, these listeners will be + * called. + * + * @param l The new listener to add + */ + public void addChangeListener(ChangeListener l) + { + listenerList.add(ChangeListener.class, l); + } + + /** + * Removes a ChangeListener from the button's listener list. + * + * @param l The listener to remove + */ + public void removeChangeListener(ChangeListener l) + { + listenerList.remove(ChangeListener.class, l); + } + + /** + * Returns all added <code>ChangeListener</code> objects. + * + * @return an array of listeners + * + * @since 1.4 + */ + public ChangeListener[] getChangeListeners() + { + return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); + } + + /** + * Calls {@link ItemListener#itemStateChanged} on each ItemListener in + * the button's listener list. + * + * @param e The event signifying that the button's model changed state + */ + protected void fireItemStateChanged(ItemEvent e) + { + e.setSource(this); + ItemListener[] listeners = getItemListeners(); + + for (int i = 0; i < listeners.length; i++) + listeners[i].itemStateChanged(e); + } + + /** + * Calls {@link ActionListener#actionPerformed} on each {@link + * ActionListener} in the button's listener list. + * + * @param e The event signifying that the button's model was clicked + */ + protected void fireActionPerformed(ActionEvent e) + { + // Dispatch a copy of the given ActionEvent in order to + // set the source and action command correctly. + ActionEvent ae = new ActionEvent( + this, + e.getID(), + getActionCommand(), + e.getWhen(), + e.getModifiers()); + + ActionListener[] listeners = getActionListeners(); + + for (int i = 0; i < listeners.length; i++) + listeners[i].actionPerformed(ae); + } + + /** + * Calls {@link ChangeListener#stateChanged} on each {@link ChangeListener} + * in the button's listener list. + */ + protected void fireStateChanged() + { + ChangeListener[] listeners = getChangeListeners(); + + for (int i = 0; i < listeners.length; i++) + listeners[i].stateChanged(changeEvent); + } + + /** + * Get the current keyboard mnemonic value. This value corresponds to a + * single key code (one of the {@link java.awt.event.KeyEvent} VK_* + * codes) and is used to activate the button when pressed in conjunction + * with the "mouseless modifier" of the button's look and feel class, and + * when focus is in one of the button's ancestors. + * + * @return The button's current keyboard mnemonic + */ + public int getMnemonic() + { + ButtonModel mod = getModel(); + if (mod != null) + return mod.getMnemonic(); + return -1; + } + + /** + * Set the current keyboard mnemonic value. This value corresponds to a + * single key code (one of the {@link java.awt.event.KeyEvent} VK_* + * codes) and is used to activate the button when pressed in conjunction + * with the "mouseless modifier" of the button's look and feel class, and + * when focus is in one of the button's ancestors. + * + * @param mne A new mnemonic to use for the button + */ + public void setMnemonic(char mne) + { + setMnemonic((int) mne); + } + + /** + * Set the current keyboard mnemonic value. This value corresponds to a + * single key code (one of the {@link java.awt.event.KeyEvent} VK_* + * codes) and is used to activate the button when pressed in conjunction + * with the "mouseless modifier" of the button's look and feel class, and + * when focus is in one of the button's ancestors. + * + * @param mne A new mnemonic to use for the button + */ + public void setMnemonic(int mne) + { + ButtonModel mod = getModel(); + int old = -1; + if (mod != null) + old = mod.getMnemonic(); + + if (old != mne) + { + if (mod != null) + mod.setMnemonic(mne); + + if (text != null && !text.equals("")) + { + // Since lower case char = upper case char for + // mnemonic, we will convert both text and mnemonic + // to upper case before checking if mnemonic character occurs + // in the menu item text. + int upperCaseMne = Character.toUpperCase((char) mne); + String upperCaseText = text.toUpperCase(); + setDisplayedMnemonicIndex(upperCaseText.indexOf(upperCaseMne)); + } + + firePropertyChange(MNEMONIC_CHANGED_PROPERTY, old, mne); + revalidate(); + repaint(); + } + } + + /** + * Sets the button's mnemonic index. The mnemonic index is a hint to the + * look and feel class, suggesting which character in the button's label + * should be underlined when drawing the label. If the mnemonic index is + * -1, no mnemonic will be displayed. + * + * If no mnemonic index is set, the button will choose a mnemonic index + * by default, which will be the first occurrence of the mnemonic + * character in the button's text. + * + * @param index An offset into the "text" property of the button + * @throws IllegalArgumentException If <code>index</code> is not within the + * range of legal offsets for the "text" property of the button. + * @since 1.4 + */ + + public void setDisplayedMnemonicIndex(int index) + { + if (index < -1 || (text != null && index >= text.length())) + throw new IllegalArgumentException(); + + mnemonicIndex = index; + } + + /** + * Get the button's mnemonic index, which is an offset into the button's + * "text" property. The character specified by this offset should be + * underlined when the look and feel class draws this button. + * + * @return An index into the button's "text" property + */ + public int getDisplayedMnemonicIndex() + { + return mnemonicIndex; + } + + + /** + * Set the "rolloverEnabled" property. When rollover is enabled, and the + * look and feel supports it, the button will change its icon to + * rolloverIcon, when the mouse passes over it. + * + * @param r Whether or not to enable rollover icon changes + */ + public void setRolloverEnabled(boolean r) + { + clientRolloverEnabledSet = true; + if (rollOverEnabled != r) + { + rollOverEnabled = r; + firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r); + revalidate(); + repaint(); + } + } + + /** + * Returns whether or not rollover icon changes are enabled on the + * button. + * + * @return The state of the "rolloverEnabled" property + */ + public boolean isRolloverEnabled() + { + return rollOverEnabled; + } + + /** + * Set the value of the button's "selected" property. Selection is only + * meaningful for toggle-type buttons (check boxes, radio buttons). + * + * @param s New value for the property + */ + public void setSelected(boolean s) + { + ButtonModel mod = getModel(); + if (mod != null) + mod.setSelected(s); + } + + /** + * Get the value of the button's "selected" property. Selection is only + * meaningful for toggle-type buttons (check boxes, radio buttons). + * + * @return The value of the property + */ + public boolean isSelected() + { + ButtonModel mod = getModel(); + if (mod != null) + return mod.isSelected(); + return false; + } + + /** + * Enables or disables the button. A button will neither be selectable + * nor preform any actions unless it is enabled. + * + * @param b Whether or not to enable the button + */ + public void setEnabled(boolean b) + { + // Do nothing if state does not change. + if (b == isEnabled()) + return; + super.setEnabled(b); + setFocusable(b); + ButtonModel mod = getModel(); + if (mod != null) + mod.setEnabled(b); + } + + /** + * Set the horizontal alignment of the button's text and icon. The + * alignment is a numeric constant from {@link SwingConstants}. It must + * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, + * <code>LEADING</code> or <code>TRAILING</code>. The default is + * <code>CENTER</code>. + * + * @return The current horizontal alignment + * + * @see #setHorizontalAlignment(int) + */ + public int getHorizontalAlignment() + { + return horizontalAlignment; + } + + /** + * Set the horizontal alignment of the button's text and icon. The + * alignment is a numeric constant from {@link SwingConstants}. It must + * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, + * <code>LEADING</code> or <code>TRAILING</code>. The default is + * <code>CENTER</code>. + * + * @param a The new horizontal alignment + * @throws IllegalArgumentException If alignment is not one of the legal + * constants. + * + * @see #getHorizontalAlignment() + */ + public void setHorizontalAlignment(int a) + { + if (horizontalAlignment == a) + return; + if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING + && a != TRAILING) + throw new IllegalArgumentException("Invalid alignment."); + int old = horizontalAlignment; + horizontalAlignment = a; + firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a); + revalidate(); + repaint(); + } + + /** + * Get the horizontal position of the button's text relative to its + * icon. The position is a numeric constant from {@link + * SwingConstants}. It must be one of: <code>RIGHT</code>, + * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or + * <code>TRAILING</code>. The default is <code>TRAILING</code>. + * + * @return The current horizontal text position + */ + public int getHorizontalTextPosition() + { + return horizontalTextPosition; + } + + /** + * Set the horizontal position of the button's text relative to its + * icon. The position is a numeric constant from {@link + * SwingConstants}. It must be one of: <code>RIGHT</code>, + * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or + * <code>TRAILING</code>. The default is <code>TRAILING</code>. + * + * @param t The new horizontal text position + * @throws IllegalArgumentException If position is not one of the legal + * constants. + */ + public void setHorizontalTextPosition(int t) + { + if (horizontalTextPosition == t) + return; + if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING + && t != TRAILING) + throw new IllegalArgumentException("Invalid alignment."); + + int old = horizontalTextPosition; + horizontalTextPosition = t; + firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t); + revalidate(); + repaint(); + } + + /** + * Get the vertical alignment of the button's text and icon. The + * alignment is a numeric constant from {@link SwingConstants}. It must + * be one of: <code>CENTER</code>, <code>TOP</code>, or + * <code>BOTTOM</code>. The default is <code>CENTER</code>. + * + * @return The current vertical alignment + * + * @see #setVerticalAlignment(int) + */ + public int getVerticalAlignment() + { + return verticalAlignment; + } + + /** + * Set the vertical alignment of the button's text and icon. The + * alignment is a numeric constant from {@link SwingConstants}. It must + * be one of: <code>CENTER</code>, <code>TOP</code>, or + * <code>BOTTOM</code>. The default is <code>CENTER</code>. + * + * @param a The new vertical alignment + * @throws IllegalArgumentException If alignment is not one of the legal + * constants. + * + * @see #getVerticalAlignment() + */ + public void setVerticalAlignment(int a) + { + if (verticalAlignment == a) + return; + if (a != TOP && a != CENTER && a != BOTTOM) + throw new IllegalArgumentException("Invalid alignment."); + + int old = verticalAlignment; + verticalAlignment = a; + firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a); + revalidate(); + repaint(); + } + + /** + * Get the vertical position of the button's text relative to its + * icon. The alignment is a numeric constant from {@link + * SwingConstants}. It must be one of: <code>CENTER</code>, + * <code>TOP</code>, or <code>BOTTOM</code>. The default is + * <code>CENTER</code>. + * + * @return The current vertical position + */ + public int getVerticalTextPosition() + { + return verticalTextPosition; + } + + /** + * Set the vertical position of the button's text relative to its + * icon. The alignment is a numeric constant from {@link + * SwingConstants}. It must be one of: <code>CENTER</code>, + * <code>TOP</code>, or <code>BOTTOM</code>. The default is + * <code>CENTER</code>. + * + * @param t The new vertical position + * @throws IllegalArgumentException If position is not one of the legal + * constants. + */ + public void setVerticalTextPosition(int t) + { + if (verticalTextPosition == t) + return; + if (t != TOP && t != CENTER && t != BOTTOM) + throw new IllegalArgumentException("Invalid alignment."); + + int old = verticalTextPosition; + verticalTextPosition = t; + firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t); + revalidate(); + repaint(); + } + + /** + * Set the value of the "borderPainted" property. If set to + * <code>false</code>, the button's look and feel class should not paint + * a border for the button. The default is <code>true</code>. + * + * @return The current value of the property. + */ + public boolean isBorderPainted() + { + return borderPainted; + } + + /** + * Set the value of the "borderPainted" property. If set to + * <code>false</code>, the button's look and feel class should not paint + * a border for the button. The default is <code>true</code>. + * + * @param b The new value of the property. + */ + public void setBorderPainted(boolean b) + { + clientBorderPaintedSet = true; + if (borderPainted == b) + return; + boolean old = borderPainted; + borderPainted = b; + firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b); + revalidate(); + repaint(); + } + + /** + * Get the value of the "action" property. + * + * @return The current value of the "action" property + */ + public Action getAction() + { + return action; + } + + /** + * <p>Set the button's "action" property, subscribing the new action to the + * button, as an ActionListener, if it is not already subscribed. The old + * Action, if it exists, is unsubscribed, and the button is unsubscribed + * from the old Action if it was previously subscribed as a + * PropertyChangeListener.</p> + * + * <p>This method also configures several of the button's properties from + * the Action, by calling {@link #configurePropertiesFromAction}, and + * subscribes the button to the Action as a PropertyChangeListener. + * Subsequent changes to the Action will thus reconfigure the button + * automatically.</p> + * + * @param a The new value of the "action" property + */ + public void setAction(Action a) + { + if (action != null) + { + action.removePropertyChangeListener(actionPropertyChangeListener); + removeActionListener(action); + if (actionPropertyChangeListener != null) + { + action.removePropertyChangeListener(actionPropertyChangeListener); + actionPropertyChangeListener = null; + } + } + + Action old = action; + action = a; + configurePropertiesFromAction(action); + if (action != null) + { + actionPropertyChangeListener = createActionPropertyChangeListener(a); + action.addPropertyChangeListener(actionPropertyChangeListener); + addActionListener(action); + } + } + + /** + * Return the button's default "icon" property. + * + * @return The current default icon + */ + public Icon getIcon() + { + return default_icon; + } + + /** + * Set the button's default "icon" property. This icon is used as a basis + * for the pressed and disabled icons, if none are explicitly set. + * + * @param i The new default icon + */ + public void setIcon(Icon i) + { + if (default_icon == i) + return; + + Icon old = default_icon; + default_icon = i; + firePropertyChange(ICON_CHANGED_PROPERTY, old, i); + revalidate(); + repaint(); + } + + /** + * Return the button's "text" property. This property is synonymous with + * the "label" property. + * + * @return The current "text" property + */ + public String getText() + { + return text; + } + + /** + * Set the button's "label" property. This property is synonymous with the + * "text" property. + * + * @param label The new "label" property + * + * @deprecated use <code>setText(text)</code> + */ + public void setLabel(String label) + { + setText(label); + } + + /** + * Return the button's "label" property. This property is synonymous with + * the "text" property. + * + * @return The current "label" property + * + * @deprecated use <code>getText()</code> + */ + public String getLabel() + { + return getText(); + } + + /** + * Set the button's "text" property. This property is synonymous with the + * "label" property. + * + * @param t The new "text" property + */ + public void setText(String t) + { + if (text == t) + return; + + String old = text; + text = t; + firePropertyChange(TEXT_CHANGED_PROPERTY, old, t); + revalidate(); + repaint(); + } + + /** + * Set the value of the {@link #iconTextGap} property. + * + * @param i The new value of the property + * + * @since 1.4 + */ + public void setIconTextGap(int i) + { + clientIconTextGapSet = true; + if (iconTextGap == i) + return; + + int old = iconTextGap; + iconTextGap = i; + firePropertyChange("iconTextGap", new Integer(old), new Integer(i)); + revalidate(); + repaint(); + } + + /** + * Get the value of the {@link #iconTextGap} property. + * + * @return The current value of the property + * + * @since 1.4 + */ + public int getIconTextGap() + { + return iconTextGap; + } + + /** + * Return the button's "margin" property, which is an {@link Insets} object + * describing the distance between the button's border and its text and + * icon. + * + * @return The current "margin" property + */ + public Insets getMargin() + { + return margin; + } + + /** + * Set the button's "margin" property, which is an {@link Insets} object + * describing the distance between the button's border and its text and + * icon. + * + * @param m The new "margin" property + */ + public void setMargin(Insets m) + { + if (margin == m) + return; + + Insets old = margin; + margin = m; + firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m); + revalidate(); + repaint(); + } + + /** + * Return the button's "pressedIcon" property. The look and feel class + * should paint this icon when the "pressed" property of the button's + * {@link ButtonModel} is <code>true</code>. This property may be + * <code>null</code>, in which case the default icon is used. + * + * @return The current "pressedIcon" property + */ + public Icon getPressedIcon() + { + return pressed_icon; + } + + /** + * Set the button's "pressedIcon" property. The look and feel class + * should paint this icon when the "pressed" property of the button's + * {@link ButtonModel} is <code>true</code>. This property may be + * <code>null</code>, in which case the default icon is used. + * + * @param pressedIcon The new "pressedIcon" property + */ + public void setPressedIcon(Icon pressedIcon) + { + if (pressed_icon == pressedIcon) + return; + + Icon old = pressed_icon; + pressed_icon = pressedIcon; + firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon); + revalidate(); + repaint(); + } + + /** + * Return the button's "disabledIcon" property. The look and feel class + * should paint this icon when the "enabled" property of the button's + * {@link ButtonModel} is <code>false</code>. This property may be + * <code>null</code>, in which case an icon is constructed, based on the + * default icon. + * + * @return The current "disabledIcon" property + */ + public Icon getDisabledIcon() + { + if (disabledIcon == null && default_icon instanceof ImageIcon) + { + Image iconImage = ((ImageIcon) default_icon).getImage(); + Image grayImage = GrayFilter.createDisabledImage(iconImage); + disabledIcon = new ImageIcon(grayImage); + } + + return disabledIcon; + } + + /** + * Set the button's "disabledIcon" property. The look and feel class should + * paint this icon when the "enabled" property of the button's {@link + * ButtonModel} is <code>false</code>. This property may be + * <code>null</code>, in which case an icon is constructed, based on the + * default icon. + * + * @param d The new "disabledIcon" property + */ + public void setDisabledIcon(Icon d) + { + if (disabledIcon == d) + return; + Icon old = disabledIcon; + disabledIcon = d; + firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d); + revalidate(); + repaint(); + } + + /** + * Return the button's "paintFocus" property. This property controls + * whether or not the look and feel class will paint a special indicator + * of focus state for the button. If it is false, the button still paints + * when focused, but no special decoration is painted to indicate the + * presence of focus. + * + * @return The current "paintFocus" property + */ + public boolean isFocusPainted() + { + return focusPainted; + } + + /** + * Set the button's "paintFocus" property. This property controls whether + * or not the look and feel class will paint a special indicator of focus + * state for the button. If it is false, the button still paints when + * focused, but no special decoration is painted to indicate the presence + * of focus. + * + * @param p The new "paintFocus" property + */ + public void setFocusPainted(boolean p) + { + if (focusPainted == p) + return; + + boolean old = focusPainted; + focusPainted = p; + firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p); + revalidate(); + repaint(); + } + + /** + * Verifies that a particular key is one of the valid constants used for + * describing horizontal alignment and positioning. The valid constants + * are the following members of {@link SwingConstants}: + * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, + * <code>LEADING</code> or <code>TRAILING</code>. + * + * @param key The key to check + * @param exception A message to include in an IllegalArgumentException + * + * @return the value of key + * + * @throws IllegalArgumentException If key is not one of the valid constants + * + * @see #setHorizontalTextPosition(int) + * @see #setHorizontalAlignment(int) + */ + protected int checkHorizontalKey(int key, String exception) + { + switch (key) + { + case SwingConstants.RIGHT: + case SwingConstants.LEFT: + case SwingConstants.CENTER: + case SwingConstants.LEADING: + case SwingConstants.TRAILING: + break; + default: + throw new IllegalArgumentException(exception); + } + return key; + } + + /** + * Verifies that a particular key is one of the valid constants used for + * describing vertical alignment and positioning. The valid constants are + * the following members of {@link SwingConstants}: <code>TOP</code>, + * <code>BOTTOM</code> or <code>CENTER</code>. + * + * @param key The key to check + * @param exception A message to include in an IllegalArgumentException + * + * @return the value of key + * + * @throws IllegalArgumentException If key is not one of the valid constants + * + * @see #setVerticalTextPosition(int) + * @see #setVerticalAlignment(int) + */ + protected int checkVerticalKey(int key, String exception) + { + switch (key) + { + case SwingConstants.TOP: + case SwingConstants.BOTTOM: + case SwingConstants.CENTER: + break; + default: + throw new IllegalArgumentException(exception); + } + return key; + } + + /** + * Configure various properties of the button by reading properties + * of an {@link Action}. The mapping of properties is as follows: + * + * <table> + * + * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr> + * + * <tr><td>NAME </td> <td>text </td></tr> + * <tr><td>SMALL_ICON </td> <td>icon </td></tr> + * <tr><td>SHORT_DESCRIPTION </td> <td>toolTipText </td></tr> + * <tr><td>MNEMONIC_KEY </td> <td>mnemonic </td></tr> + * <tr><td>ACTION_COMMAND_KEY </td> <td>actionCommand </td></tr> + * + * </table> + * + * <p>In addition, this method always sets the button's "enabled" property to + * the value of the Action's "enabled" property.</p> + * + * <p>If the provided Action is <code>null</code>, the text, icon, and + * toolTipText properties of the button are set to <code>null</code>, and + * the "enabled" property is set to <code>true</code>; the mnemonic and + * actionCommand properties are unchanged.</p> + * + * @param a An Action to configure the button from + */ + protected void configurePropertiesFromAction(Action a) + { + if (a == null) + { + setText(null); + setIcon(null); + setEnabled(true); + setToolTipText(null); + } + else + { + setText((String) (a.getValue(Action.NAME))); + setIcon((Icon) (a.getValue(Action.SMALL_ICON))); + setEnabled(a.isEnabled()); + setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION))); + if (a.getValue(Action.MNEMONIC_KEY) != null) + setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue()); + String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY)); + + // Set actionCommand to button's text by default if it is not specified + if (actionCommand != null) + setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY))); + else + setActionCommand(getText()); + } + } + + /** + * <p>A factory method which should return an {@link ActionListener} that + * propagates events from the button's {@link ButtonModel} to any of the + * button's ActionListeners. By default, this is an inner class which + * calls {@link AbstractButton#fireActionPerformed} with a modified copy + * of the incoming model {@link ActionEvent}.</p> + * + * <p>The button calls this method during construction, stores the + * resulting ActionListener in its <code>actionListener</code> member + * field, and subscribes it to the button's model. If the button's model + * is changed, this listener is unsubscribed from the old model and + * subscribed to the new one.</p> + * + * @return A new ActionListener + */ + protected ActionListener createActionListener() + { + return getEventHandler(); + } + + /** + * <p>A factory method which should return a {@link PropertyChangeListener} + * that accepts changes to the specified {@link Action} and reconfigure + * the {@link AbstractButton}, by default using the {@link + * #configurePropertiesFromAction} method.</p> + * + * <p>The button calls this method whenever a new Action is assigned to + * the button's "action" property, via {@link #setAction}, and stores the + * resulting PropertyChangeListener in its + * <code>actionPropertyChangeListener</code> member field. The button + * then subscribes the listener to the button's new action. If the + * button's action is changed subsequently, the listener is unsubscribed + * from the old action and subscribed to the new one.</p> + * + * @param a The Action which will be listened to, and which should be + * the same as the source of any PropertyChangeEvents received by the + * new listener returned from this method. + * + * @return A new PropertyChangeListener + */ + protected PropertyChangeListener createActionPropertyChangeListener(Action a) + { + return new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent e) + { + Action act = (Action) (e.getSource()); + if (e.getPropertyName().equals("enabled")) + setEnabled(act.isEnabled()); + else if (e.getPropertyName().equals(Action.NAME)) + setText((String) (act.getValue(Action.NAME))); + else if (e.getPropertyName().equals(Action.SMALL_ICON)) + setIcon((Icon) (act.getValue(Action.SMALL_ICON))); + else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION)) + setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION))); + else if (e.getPropertyName().equals(Action.MNEMONIC_KEY)) + if (act.getValue(Action.MNEMONIC_KEY) != null) + setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY))) + .intValue()); + else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY)) + setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY))); + } + }; + } + + /** + * <p>Factory method which creates a {@link ChangeListener}, used to + * subscribe to ChangeEvents from the button's model. Subclasses of + * AbstractButton may wish to override the listener used to subscribe to + * such ChangeEvents. By default, the listener just propagates the + * {@link ChangeEvent} to the button's ChangeListeners, via the {@link + * AbstractButton#fireStateChanged} method.</p> + * + * <p>The button calls this method during construction, stores the + * resulting ChangeListener in its <code>changeListener</code> member + * field, and subscribes it to the button's model. If the button's model + * is changed, this listener is unsubscribed from the old model and + * subscribed to the new one.</p> + * + * @return The new ChangeListener + */ + protected ChangeListener createChangeListener() + { + return getEventHandler(); + } + + /** + * <p>Factory method which creates a {@link ItemListener}, used to + * subscribe to ItemEvents from the button's model. Subclasses of + * AbstractButton may wish to override the listener used to subscribe to + * such ItemEvents. By default, the listener just propagates the + * {@link ItemEvent} to the button's ItemListeners, via the {@link + * AbstractButton#fireItemStateChanged} method.</p> + * + * <p>The button calls this method during construction, stores the + * resulting ItemListener in its <code>changeListener</code> member + * field, and subscribes it to the button's model. If the button's model + * is changed, this listener is unsubscribed from the old model and + * subscribed to the new one.</p> + * + * <p>Note that ItemEvents are only generated from the button's model + * when the model's <em>selected</em> property changes. If you want to + * subscribe to other properties of the model, you must subscribe to + * ChangeEvents. + * + * @return The new ItemListener + */ + protected ItemListener createItemListener() + { + return getEventHandler(); + } + + /** + * Programmatically perform a "click" on the button: arming, pressing, + * waiting, un-pressing, and disarming the model. + */ + public void doClick() + { + doClick(100); + } + + /** + * Programmatically perform a "click" on the button: arming, pressing, + * waiting, un-pressing, and disarming the model. + * + * @param pressTime The number of milliseconds to wait in the pressed state + */ + public void doClick(int pressTime) + { + ButtonModel mod = getModel(); + if (mod != null) + { + mod.setArmed(true); + mod.setPressed(true); + try + { + java.lang.Thread.sleep(pressTime); + } + catch (java.lang.InterruptedException e) + { + // probably harmless + } + mod.setPressed(false); + mod.setArmed(false); + } + } + + /** + * Return the button's disabled selected icon. The look and feel class + * should paint this icon when the "enabled" property of the button's model + * is <code>false</code> and its "selected" property is + * <code>true</code>. This icon can be <code>null</code>, in which case + * it is synthesized from the button's selected icon. + * + * @return The current disabled selected icon + */ + public Icon getDisabledSelectedIcon() + { + return disabledSelectedIcon; + } + + /** + * Set the button's disabled selected icon. The look and feel class + * should paint this icon when the "enabled" property of the button's model + * is <code>false</code> and its "selected" property is + * <code>true</code>. This icon can be <code>null</code>, in which case + * it is synthesized from the button's selected icon. + * + * @param icon The new disabled selected icon + */ + public void setDisabledSelectedIcon(Icon icon) + { + if (disabledSelectedIcon == icon) + return; + + Icon old = disabledSelectedIcon; + disabledSelectedIcon = icon; + firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon); + revalidate(); + repaint(); + } + + /** + * Return the button's rollover icon. The look and feel class should + * paint this icon when the "rolloverEnabled" property of the button is + * <code>true</code> and the mouse rolls over the button. + * + * @return The current rollover icon + */ + public Icon getRolloverIcon() + { + return rolloverIcon; + } + + /** + * Set the button's rollover icon and sets the <code>rolloverEnabled</code> + * property to <code>true</code>. The look and feel class should + * paint this icon when the "rolloverEnabled" property of the button is + * <code>true</code> and the mouse rolls over the button. + * + * @param r The new rollover icon + */ + public void setRolloverIcon(Icon r) + { + if (rolloverIcon == r) + return; + + Icon old = rolloverIcon; + rolloverIcon = r; + firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon); + setRolloverEnabled(true); + revalidate(); + repaint(); + } + + /** + * Return the button's rollover selected icon. The look and feel class + * should paint this icon when the "rolloverEnabled" property of the button + * is <code>true</code>, the "selected" property of the button's model is + * <code>true</code>, and the mouse rolls over the button. + * + * @return The current rollover selected icon + */ + public Icon getRolloverSelectedIcon() + { + return rolloverSelectedIcon; + } + + /** + * Set the button's rollover selected icon and sets the + * <code>rolloverEnabled</code> property to <code>true</code>. The look and + * feel class should paint this icon when the "rolloverEnabled" property of + * the button is <code>true</code>, the "selected" property of the button's + * model is <code>true</code>, and the mouse rolls over the button. + * + * @param r The new rollover selected icon. + */ + public void setRolloverSelectedIcon(Icon r) + { + if (rolloverSelectedIcon == r) + return; + + Icon old = rolloverSelectedIcon; + rolloverSelectedIcon = r; + firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r); + setRolloverEnabled(true); + revalidate(); + repaint(); + } + + /** + * Return the button's selected icon. The look and feel class should + * paint this icon when the "selected" property of the button's model is + * <code>true</code>, and either the "rolloverEnabled" property of the + * button is <code>false</code> or the mouse is not currently rolled + * over the button. + * + * @return The current selected icon + */ + public Icon getSelectedIcon() + { + return selectedIcon; + } + + /** + * Set the button's selected icon. The look and feel class should + * paint this icon when the "selected" property of the button's model is + * <code>true</code>, and either the "rolloverEnabled" property of the + * button is <code>false</code> or the mouse is not currently rolled + * over the button. + * + * @param s The new selected icon + */ + public void setSelectedIcon(Icon s) + { + if (selectedIcon == s) + return; + + Icon old = selectedIcon; + selectedIcon = s; + firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s); + revalidate(); + repaint(); + } + + /** + * Returns an single-element array containing the "text" property of the + * button if the "selected" property of the button's model is + * <code>true</code>, otherwise returns <code>null</code>. + * + * @return The button's "selected object" array + */ + public Object[] getSelectedObjects() + { + if (isSelected()) + { + Object[] objs = new Object[1]; + objs[0] = getText(); + return objs; + } + else + { + return null; + } + } + + /** + * Called when image data becomes available for one of the button's icons. + * + * @param img The image being updated + * @param infoflags One of the constant codes in {@link ImageObserver} used + * to describe updated portions of an image. + * @param x X coordinate of the region being updated + * @param y Y coordinate of the region being updated + * @param w Width of the region beign updated + * @param h Height of the region being updated + * + * @return <code>true</code> if img is equal to the button's current icon, + * otherwise <code>false</code> + */ + public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, + int h) + { + return current_icon == img; + } + + /** + * Returns the value of the button's "contentAreaFilled" property. This + * property indicates whether the area surrounding the text and icon of + * the button should be filled by the look and feel class. If this + * property is <code>false</code>, the look and feel class should leave + * the content area transparent. + * + * @return The current value of the "contentAreaFilled" property + */ + public boolean isContentAreaFilled() + { + return contentAreaFilled; + } + + /** + * Sets the value of the button's "contentAreaFilled" property. This + * property indicates whether the area surrounding the text and icon of + * the button should be filled by the look and feel class. If this + * property is <code>false</code>, the look and feel class should leave + * the content area transparent. + * + * @param b The new value of the "contentAreaFilled" property + */ + public void setContentAreaFilled(boolean b) + { + clientContentAreaFilledSet = true; + if (contentAreaFilled == b) + return; + + // The JDK sets the opaque property to the value of the contentAreaFilled + // property, so should we do. + setOpaque(b); + boolean old = contentAreaFilled; + contentAreaFilled = b; + firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b); + } + + /** + * Paints the button's border, if the button's "borderPainted" property is + * <code>true</code>, by out calling to the button's look and feel class. + * + * @param g The graphics context used to paint the border + */ + protected void paintBorder(Graphics g) + { + if (isBorderPainted()) + super.paintBorder(g); + } + + /** + * Returns a string, used only for debugging, which identifies or somehow + * represents this button. The exact value is implementation-defined. + * + * @return A string representation of the button + */ + protected String paramString() + { + CPStringBuilder sb = new CPStringBuilder(); + sb.append(super.paramString()); + sb.append(",defaultIcon="); + if (getIcon() != null) + sb.append(getIcon()); + sb.append(",disabledIcon="); + if (getDisabledIcon() != null) + sb.append(getDisabledIcon()); + sb.append(",disabledSelectedIcon="); + if (getDisabledSelectedIcon() != null) + sb.append(getDisabledSelectedIcon()); + sb.append(",margin="); + if (getMargin() != null) + sb.append(getMargin()); + sb.append(",paintBorder=").append(isBorderPainted()); + sb.append(",paintFocus=").append(isFocusPainted()); + sb.append(",pressedIcon="); + if (getPressedIcon() != null) + sb.append(getPressedIcon()); + sb.append(",rolloverEnabled=").append(isRolloverEnabled()); + sb.append(",rolloverIcon="); + if (getRolloverIcon() != null) + sb.append(getRolloverIcon()); + sb.append(",rolloverSelected="); + if (getRolloverSelectedIcon() != null) + sb.append(getRolloverSelectedIcon()); + sb.append(",selectedIcon="); + if (getSelectedIcon() != null) + sb.append(getSelectedIcon()); + sb.append(",text="); + if (getText() != null) + sb.append(getText()); + return sb.toString(); + } + + /** + * Set the "UI" property of the button, which is a look and feel class + * responsible for handling the button's input events and painting it. + * + * @param ui The new "UI" property + */ + public void setUI(ButtonUI ui) + { + super.setUI(ui); + } + + /** + * Set the "UI" property of the button, which is a look and feel class + * responsible for handling the button's input events and painting it. + * + * @return The current "UI" property + */ + public ButtonUI getUI() + { + return (ButtonUI) ui; + } + + /** + * Set the "UI" property to a class constructed, via the {@link + * UIManager}, from the current look and feel. This should be overridden + * for each subclass of AbstractButton, to retrieve a suitable {@link + * ButtonUI} look and feel class. + */ + public void updateUI() + { + // TODO: What to do here? + } + + /** + * Returns the current time in milliseconds in which clicks gets coalesced + * into a single <code>ActionEvent</code>. + * + * @return the time in milliseconds + * + * @since 1.4 + */ + public long getMultiClickThreshhold() + { + return multiClickThreshhold; + } + + /** + * Sets the time in milliseconds in which clicks gets coalesced into a single + * <code>ActionEvent</code>. + * + * @param threshhold the time in milliseconds + * + * @since 1.4 + */ + public void setMultiClickThreshhold(long threshhold) + { + if (threshhold < 0) + throw new IllegalArgumentException(); + + multiClickThreshhold = threshhold; + } + + /** + * Adds the specified component to this AbstractButton. This overrides the + * default in order to install an {@link OverlayLayout} layout manager + * before adding the component. The layout manager is only installed if + * no other layout manager has been installed before. + * + * @param comp the component to be added + * @param constraints constraints for the layout manager + * @param index the index at which the component is added + * + * @since 1.5 + */ + protected void addImpl(Component comp, Object constraints, int index) + { + // We use a client property here, so that no extra memory is used in + // the common case with no layout manager. + if (getClientProperty("AbstractButton.customLayoutSet") == null) + setLayout(new OverlayLayout(this)); + super.addImpl(comp, constraints, index); + } + + /** + * Sets a layout manager on this AbstractButton. This is overridden in order + * to detect if the application sets a custom layout manager. If no custom + * layout manager is set, {@link #addImpl(Component, Object, int)} installs + * an OverlayLayout before adding a component. + * + * @param layout the layout manager to install + * + * @since 1.5 + */ + public void setLayout(LayoutManager layout) + { + // We use a client property here, so that no extra memory is used in + // the common case with no layout manager. + putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE); + super.setLayout(layout); + } + + /** + * Helper method for + * {@link LookAndFeel#installProperty(JComponent, String, Object)}. + * + * @param propertyName the name of the property + * @param value the value of the property + * + * @throws IllegalArgumentException if the specified property cannot be set + * by this method + * @throws ClassCastException if the property value does not match the + * property type + * @throws NullPointerException if <code>c</code> or + * <code>propertyValue</code> is <code>null</code> + */ + void setUIProperty(String propertyName, Object value) + { + if (propertyName.equals("borderPainted")) + { + if (! clientBorderPaintedSet) + { + setBorderPainted(((Boolean) value).booleanValue()); + clientBorderPaintedSet = false; + } + } + else if (propertyName.equals("rolloverEnabled")) + { + if (! clientRolloverEnabledSet) + { + setRolloverEnabled(((Boolean) value).booleanValue()); + clientRolloverEnabledSet = false; + } + } + else if (propertyName.equals("iconTextGap")) + { + if (! clientIconTextGapSet) + { + setIconTextGap(((Integer) value).intValue()); + clientIconTextGapSet = false; + } + } + else if (propertyName.equals("contentAreaFilled")) + { + if (! clientContentAreaFilledSet) + { + setContentAreaFilled(((Boolean) value).booleanValue()); + clientContentAreaFilledSet = false; + } + } + else + { + super.setUIProperty(propertyName, value); + } + } + + /** + * Returns the combined event handler. The instance is created if + * necessary. + * + * @return the combined event handler + */ + EventHandler getEventHandler() + { + if (eventHandler == null) + eventHandler = new EventHandler(); + return eventHandler; + } +} |