1 /* AbstractButton.java -- Provides basic button functionality.
2 Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
40 import gnu.classpath.NotImplementedException;
42 import java.awt.Component;
43 import java.awt.Graphics;
44 import java.awt.Image;
45 import java.awt.Insets;
46 import java.awt.ItemSelectable;
47 import java.awt.LayoutManager;
48 import java.awt.Point;
49 import java.awt.Rectangle;
50 import java.awt.Shape;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.awt.event.ItemEvent;
54 import java.awt.event.ItemListener;
55 import java.awt.image.ImageObserver;
56 import java.beans.PropertyChangeEvent;
57 import java.beans.PropertyChangeListener;
58 import java.io.Serializable;
59 import java.util.Enumeration;
61 import javax.accessibility.Accessible;
62 import javax.accessibility.AccessibleAction;
63 import javax.accessibility.AccessibleContext;
64 import javax.accessibility.AccessibleIcon;
65 import javax.accessibility.AccessibleRelation;
66 import javax.accessibility.AccessibleRelationSet;
67 import javax.accessibility.AccessibleState;
68 import javax.accessibility.AccessibleStateSet;
69 import javax.accessibility.AccessibleText;
70 import javax.accessibility.AccessibleValue;
71 import javax.swing.event.ChangeEvent;
72 import javax.swing.event.ChangeListener;
73 import javax.swing.plaf.ButtonUI;
74 import javax.swing.plaf.basic.BasicHTML;
75 import javax.swing.text.AttributeSet;
76 import javax.swing.text.BadLocationException;
77 import javax.swing.text.Position;
78 import javax.swing.text.View;
82 * Provides an abstract implementation of common button behaviour,
83 * data model and look & feel.
85 * <p>This class is supposed to serve as a base class for
86 * several kinds of buttons with similar but non-identical semantics:
87 * toggle buttons (radio buttons and checkboxes), simple push buttons,
88 * menu items, etc.</p>
90 * <p>Buttons have many properties, some of which are stored in this class
91 * while others are delegated to the button's model. The following properties
95 * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr>
97 * <tr><td>action </td><td>button</td> <td>no</td></tr>
98 * <tr><td>actionCommand </td><td>model</td> <td>no</td></tr>
99 * <tr><td>borderPainted </td><td>button</td> <td>yes</td></tr>
100 * <tr><td>contentAreaFilled </td><td>button</td> <td>yes</td></tr>
101 * <tr><td>disabledIcon </td><td>button</td> <td>yes</td></tr>
102 * <tr><td>disabledSelectedIcon </td><td>button</td> <td>yes</td></tr>
103 * <tr><td>displayedMnemonicIndex </td><td>button</td> <td>no</td></tr>
104 * <tr><td>enabled </td><td>model</td> <td>no</td></tr>
105 * <tr><td>focusPainted </td><td>button</td> <td>yes</td></tr>
106 * <tr><td>horizontalAlignment </td><td>button</td> <td>yes</td></tr>
107 * <tr><td>horizontalTextPosition </td><td>button</td> <td>yes</td></tr>
108 * <tr><td>icon </td><td>button</td> <td>yes</td></tr>
109 * <tr><td>iconTextGap </td><td>button</td> <td>no</td></tr>
110 * <tr><td>label (same as text) </td><td>model</td> <td>yes</td></tr>
111 * <tr><td>margin </td><td>button</td> <td>yes</td></tr>
112 * <tr><td>multiClickThreshold </td><td>button</td> <td>no</td></tr>
113 * <tr><td>pressedIcon </td><td>button</td> <td>yes</td></tr>
114 * <tr><td>rolloverEnabled </td><td>button</td> <td>yes</td></tr>
115 * <tr><td>rolloverIcon </td><td>button</td> <td>yes</td></tr>
116 * <tr><td>rolloverSelectedIcon </td><td>button</td> <td>yes</td></tr>
117 * <tr><td>selected </td><td>model</td> <td>no</td></tr>
118 * <tr><td>selectedIcon </td><td>button</td> <td>yes</td></tr>
119 * <tr><td>selectedObjects </td><td>button</td> <td>no</td></tr>
120 * <tr><td>text </td><td>model</td> <td>yes</td></tr>
121 * <tr><td>UI </td><td>button</td> <td>yes</td></tr>
122 * <tr><td>verticalAlignment </td><td>button</td> <td>yes</td></tr>
123 * <tr><td>verticalTextPosition </td><td>button</td> <td>yes</td></tr>
127 * <p>The various behavioral aspects of these properties follows:</p>
131 * <li>When non-bound properties stored in the button change, the button
132 * fires ChangeEvents to its ChangeListeners.</li>
134 * <li>When bound properties stored in the button change, the button fires
135 * PropertyChangeEvents to its PropertyChangeListeners</li>
137 * <li>If any of the model's properties change, it fires a ChangeEvent to
138 * its ChangeListeners, which include the button.</li>
140 * <li>If the button receives a ChangeEvent from its model, it will
141 * propagate the ChangeEvent to its ChangeListeners, with the ChangeEvent's
142 * "source" property set to refer to the button, rather than the model. The
143 * the button will request a repaint, to paint its updated state.</li>
145 * <li>If the model's "selected" property changes, the model will fire an
146 * ItemEvent to its ItemListeners, which include the button, in addition to
147 * the ChangeEvent which models the property change. The button propagates
148 * ItemEvents directly to its ItemListeners.</li>
150 * <li>If the model's armed and pressed properties are simultaneously
151 * <code>true</code>, the model will fire an ActionEvent to its
152 * ActionListeners, which include the button. The button will propagate
153 * this ActionEvent to its ActionListeners, with the ActionEvent's "source"
154 * property set to refer to the button, rather than the model.</li>
158 * @author Ronald Veldema (rveldema@cs.vu.nl)
159 * @author Graydon Hoare (graydon@redhat.com)
162 public abstract class AbstractButton extends JComponent
163 implements ItemSelectable, SwingConstants
165 private static final long serialVersionUID = -937921345538462020L;
168 * An extension of ChangeListener to be serializable.
170 protected class ButtonChangeListener
171 implements ChangeListener, Serializable
173 private static final long serialVersionUID = 1471056094226600578L;
176 * The spec has no public/protected constructor for this class, so do we.
178 ButtonChangeListener()
180 // Nothing to do here.
184 * Notified when the target of the listener changes its state.
186 * @param ev the ChangeEvent describing the change
188 public void stateChanged(ChangeEvent ev)
190 AbstractButton.this.fireStateChanged();
195 /** The icon displayed by default. */
198 /** The icon displayed when the button is pressed. */
201 /** The icon displayed when the button is disabled. */
204 /** The icon displayed when the button is selected. */
207 /** The icon displayed when the button is selected but disabled. */
208 Icon disabledSelectedIcon;
210 /** The icon displayed when the button is rolled over. */
213 /** The icon displayed when the button is selected and rolled over. */
214 Icon rolloverSelectedIcon;
216 /** The icon currently displayed. */
219 /** The text displayed in the button. */
223 * The gap between icon and text, if both icon and text are
224 * non-<code>null</code>.
228 /** The vertical alignment of the button's text and icon. */
229 int verticalAlignment;
231 /** The horizontal alignment of the button's text and icon. */
232 int horizontalAlignment;
234 /** The horizontal position of the button's text relative to its icon. */
235 int horizontalTextPosition;
237 /** The vertical position of the button's text relative to its icon. */
238 int verticalTextPosition;
240 /** Whether or not the button paints its border. */
241 boolean borderPainted;
243 /** Whether or not the button paints its focus state. */
244 boolean focusPainted;
246 /** Whether or not the button fills its content area. */
247 boolean contentAreaFilled;
249 /** Whether rollover is enabled. */
250 boolean rollOverEnabled;
252 /** The action taken when the button is clicked. */
255 /** The button's current state. */
256 protected ButtonModel model;
258 /** The margin between the button's border and its label. */
262 * A hint to the look and feel class, suggesting which character in the
263 * button's label should be underlined when drawing the label.
267 /** Listener the button uses to receive ActionEvents from its model. */
268 protected ActionListener actionListener;
270 /** Listener the button uses to receive ItemEvents from its model. */
271 protected ItemListener itemListener;
273 /** Listener the button uses to receive ChangeEvents from its model. */
274 protected ChangeListener changeListener;
277 * The time in milliseconds in which clicks get coalesced into a single
278 * <code>ActionEvent</code>.
280 long multiClickThreshhold;
283 * Listener the button uses to receive PropertyChangeEvents from its
286 PropertyChangeListener actionPropertyChangeListener;
288 /** ChangeEvent that is fired to button's ChangeEventListeners */
289 protected ChangeEvent changeEvent = new ChangeEvent(this);
292 * Indicates if the borderPainted property has been set by a client
293 * program or by the UI.
295 * @see #setUIProperty(String, Object)
296 * @see LookAndFeel#installProperty(JComponent, String, Object)
298 private boolean clientBorderPaintedSet = false;
301 * Indicates if the rolloverEnabled property has been set by a client
302 * program or by the UI.
304 * @see #setUIProperty(String, Object)
305 * @see LookAndFeel#installProperty(JComponent, String, Object)
307 private boolean clientRolloverEnabledSet = false;
310 * Indicates if the iconTextGap property has been set by a client
311 * program or by the UI.
313 * @see #setUIProperty(String, Object)
314 * @see LookAndFeel#installProperty(JComponent, String, Object)
316 private boolean clientIconTextGapSet = false;
319 * Indicates if the contentAreaFilled property has been set by a client
320 * program or by the UI.
322 * @see #setUIProperty(String, Object)
323 * @see LookAndFeel#installProperty(JComponent, String, Object)
325 private boolean clientContentAreaFilledSet = false;
328 * Fired in a PropertyChangeEvent when the "borderPainted" property changes.
330 public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
333 * Fired in a PropertyChangeEvent when the "contentAreaFilled" property
336 public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY =
340 * Fired in a PropertyChangeEvent when the "disabledIcon" property changes.
342 public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
345 * Fired in a PropertyChangeEvent when the "disabledSelectedIcon" property
348 public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY =
349 "disabledSelectedIcon";
352 * Fired in a PropertyChangeEvent when the "focusPainted" property changes.
354 public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
357 * Fired in a PropertyChangeEvent when the "horizontalAlignment" property
360 public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY =
361 "horizontalAlignment";
364 * Fired in a PropertyChangeEvent when the "horizontalTextPosition" property
367 public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY =
368 "horizontalTextPosition";
371 * Fired in a PropertyChangeEvent when the "icon" property changes. */
372 public static final String ICON_CHANGED_PROPERTY = "icon";
374 /** Fired in a PropertyChangeEvent when the "margin" property changes. */
375 public static final String MARGIN_CHANGED_PROPERTY = "margin";
377 /** Fired in a PropertyChangeEvent when the "mnemonic" property changes. */
378 public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
380 /** Fired in a PropertyChangeEvent when the "model" property changes. */
381 public static final String MODEL_CHANGED_PROPERTY = "model";
383 /** Fired in a PropertyChangeEvent when the "pressedIcon" property changes. */
384 public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
387 * Fired in a PropertyChangeEvent when the "rolloverEnabled" property
390 public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY =
394 * Fired in a PropertyChangeEvent when the "rolloverIcon" property changes.
396 public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
399 * Fired in a PropertyChangeEvent when the "rolloverSelectedIcon" property
402 public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY =
403 "rolloverSelectedIcon";
406 * Fired in a PropertyChangeEvent when the "selectedIcon" property changes.
408 public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
410 /** Fired in a PropertyChangeEvent when the "text" property changes. */
411 public static final String TEXT_CHANGED_PROPERTY = "text";
414 * Fired in a PropertyChangeEvent when the "verticalAlignment" property
417 public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY =
421 * Fired in a PropertyChangeEvent when the "verticalTextPosition" property
424 public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY =
425 "verticalTextPosition";
428 * A Java Accessibility extension of the AbstractButton.
430 protected abstract class AccessibleAbstractButton
431 extends AccessibleJComponent implements AccessibleAction, AccessibleValue,
434 private static final long serialVersionUID = -5673062525319836790L;
436 protected AccessibleAbstractButton()
438 // Nothing to do here yet.
442 * Returns the accessible state set of this object. In addition to the
443 * superclass's states, the <code>AccessibleAbstractButton</code>
444 * supports the following states: {@link AccessibleState#ARMED},
445 * {@link AccessibleState#FOCUSED}, {@link AccessibleState#PRESSED} and
446 * {@link AccessibleState#CHECKED}.
448 * @return the current state of this accessible object
450 public AccessibleStateSet getAccessibleStateSet()
452 AccessibleStateSet state = super.getAccessibleStateSet();
454 if (getModel().isArmed())
455 state.add(AccessibleState.ARMED);
456 if (getModel().isPressed())
457 state.add(AccessibleState.PRESSED);
459 state.add(AccessibleState.CHECKED);
465 * Returns the accessible name for the button.
467 public String getAccessibleName()
469 String result = super.getAccessibleName();
476 * Returns the accessible icons of this object. If the AbstractButton's
477 * icon is an Accessible, and it's AccessibleContext is an AccessibleIcon,
478 * then this AccessibleIcon is returned, otherwise <code>null</code>.
480 * @return the accessible icons of this object, or <code>null</code> if
481 * there is no accessible icon
483 public AccessibleIcon[] getAccessibleIcon()
485 AccessibleIcon[] ret = null;
486 Icon icon = getIcon();
487 if (icon instanceof Accessible)
489 AccessibleContext ctx = ((Accessible) icon).getAccessibleContext();
490 if (ctx instanceof AccessibleIcon)
492 ret = new AccessibleIcon[]{ (AccessibleIcon) ctx };
499 * Returns the accessible relations of this AccessibleAbstractButton.
500 * If the AbstractButton is part of a ButtonGroup, then all the buttons
501 * in this button group are added as targets in a MEMBER_OF relation,
502 * otherwise an empty relation set is returned (from super).
504 * @return the accessible relations of this AccessibleAbstractButton
506 public AccessibleRelationSet getAccessibleRelationSet()
508 AccessibleRelationSet relations = super.getAccessibleRelationSet();
509 ButtonModel model = getModel();
510 if (model instanceof DefaultButtonModel)
512 ButtonGroup group = ((DefaultButtonModel) model).getGroup();
515 Object[] target = new Object[group.getButtonCount()];
516 Enumeration els = group.getElements();
518 for (int index = 0; els.hasMoreElements(); ++index)
520 target[index] = els.nextElement();
523 AccessibleRelation rel =
524 new AccessibleRelation(AccessibleRelation.MEMBER_OF);
525 rel.setTarget(target);
533 * Returns the accessible action associated with this object. For buttons,
534 * this will be <code>this</code>.
536 * @return <code>this</code>
538 public AccessibleAction getAccessibleAction()
544 * Returns the accessible value of this AccessibleAbstractButton, which
545 * is always <code>this</code>.
547 * @return the accessible value of this AccessibleAbstractButton, which
548 * is always <code>this</code>
550 public AccessibleValue getAccessibleValue()
556 * Returns the number of accessible actions that are supported by this
557 * object. Buttons support one action by default ('press button'), so this
558 * method always returns <code>1</code>.
560 * @return <code>1</code>, the number of supported accessible actions
562 public int getAccessibleActionCount()
568 * Returns a description for the action with the specified index or
569 * <code>null</code> if such action does not exist.
571 * @param actionIndex the zero based index to the actions
573 * @return a description for the action with the specified index or
574 * <code>null</code> if such action does not exist
576 public String getAccessibleActionDescription(int actionIndex)
579 if (actionIndex == 0)
581 // FIXME: Supply localized descriptions in the UIDefaults.
582 descr = UIManager.getString("AbstractButton.clickText");
588 * Performs the acccessible action with the specified index on this object.
589 * Since buttons have only one action by default (which is to press the
590 * button), this method performs a 'press button' when the specified index
591 * is <code>0</code> and nothing otherwise.
593 * @param actionIndex a zero based index into the actions of this button
595 * @return <code>true</code> if the specified action has been performed
596 * successfully, <code>false</code> otherwise
598 public boolean doAccessibleAction(int actionIndex)
600 boolean retVal = false;
601 if (actionIndex == 0)
610 * Returns the current value of this object as a number. This
611 * implementation returns an <code>Integer(1)</code> if the button is
612 * selected, <code>Integer(0)</code> if the button is not selected.
614 * @return the current value of this object as a number
616 public Number getCurrentAccessibleValue()
620 retVal = new Integer(1);
622 retVal = new Integer(0);
627 * Sets the current accessible value as object. If the specified number
628 * is 0 the button will be deselected, otherwise the button will
631 * @param value 0 for deselected button, other for selected button
633 * @return <code>true</code> if the value has been set, <code>false</code>
636 public boolean setCurrentAccessibleValue(Number value)
638 boolean retVal = false;
641 if (value.intValue() == 0)
651 * Returns the minimum accessible value for the AccessibleAbstractButton,
652 * which is <code>0</code>.
654 * @return the minimimum accessible value for the AccessibleAbstractButton,
655 * which is <code>0</code>
657 public Number getMinimumAccessibleValue()
659 return new Integer(0);
663 * Returns the maximum accessible value for the AccessibleAbstractButton,
664 * which is <code>1</code>.
666 * @return the maximum accessible value for the AccessibleAbstractButton,
667 * which is <code>1</code>
669 public Number getMaximumAccessibleValue()
671 return new Integer(1);
675 * Returns the accessible text for this AccessibleAbstractButton. This
676 * will be <code>null</code> if the button has a non-HTML label, otherwise
679 * @return the accessible text for this AccessibleAbstractButton
681 public AccessibleText getAccessibleText()
683 AccessibleText accessibleText = null;
684 if (getClientProperty(BasicHTML.propertyKey) != null)
685 accessibleText = this;
687 return accessibleText;
691 * Returns the index of the label's character at the specified point,
692 * relative to the local bounds of the button. This only works for
695 * @param p the point, relative to the buttons local bounds
697 * @return the index of the label's character at the specified point
699 public int getIndexAtPoint(Point p)
702 View view = (View) getClientProperty(BasicHTML.propertyKey);
705 Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
706 index = view.viewToModel(p.x, p.y, shape, new Position.Bias[1]);
712 * Returns the bounds of the character at the specified index of the
713 * button's label. This will only work for HTML labels.
715 * @param i the index of the character of the label
717 * @return the bounds of the character at the specified index of the
720 public Rectangle getCharacterBounds(int i)
722 Rectangle rect = null;
723 View view = (View) getClientProperty(BasicHTML.propertyKey);
726 Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
729 Shape s = view.modelToView(i, shape, Position.Bias.Forward);
730 rect = s.getBounds();
732 catch (BadLocationException ex)
741 * Returns the number of characters in the button's label.
743 * @return the bounds of the character at the specified index of the
746 public int getCharCount()
749 View view = (View) getClientProperty(BasicHTML.propertyKey);
752 charCount = view.getDocument().getLength();
756 charCount = getAccessibleName().length();
762 * This always returns <code>-1</code> since there is no caret in a button.
764 * @return <code>-1</code> since there is no caret in a button
766 public int getCaretPosition()
771 public String getAtIndex(int value0, int value1)
772 throws NotImplementedException
777 public String getAfterIndex(int value0, int value1)
778 throws NotImplementedException
783 public String getBeforeIndex(int value0, int value1)
784 throws NotImplementedException
790 * Returns the text attribute for the character at the specified character
793 * @param i the character index
795 * @return the character attributes for the specified character or
796 * <code>null</code> if the character has no attributes
798 public AttributeSet getCharacterAttribute(int i)
800 AttributeSet atts = null;
801 View view = (View) getClientProperty(BasicHTML.propertyKey);
810 * This always returns <code>-1</code> since
811 * button labels can't be selected.
813 * @return <code>-1</code>, button labels can't be selected
815 public int getSelectionStart()
821 * This always returns <code>-1</code> since
822 * button labels can't be selected.
824 * @return <code>-1</code>, button labels can't be selected
826 public int getSelectionEnd()
832 * Returns the selected text. This always returns <code>null</code> since
833 * button labels can't be selected.
835 * @return <code>null</code>, button labels can't be selected
837 public String getSelectedText()
844 * Creates a new AbstractButton object. Subclasses should call the following
845 * sequence in their constructor in order to initialize the button correctly:
851 * The {@link #init(String, Icon)} method is not called automatically by this
854 * @see #init(String, Icon)
856 public AbstractButton()
858 actionListener = createActionListener();
859 changeListener = createChangeListener();
860 itemListener = createItemListener();
862 horizontalAlignment = CENTER;
863 horizontalTextPosition = TRAILING;
864 verticalAlignment = CENTER;
865 verticalTextPosition = CENTER;
866 borderPainted = true;
867 contentAreaFilled = true;
870 setAlignmentX(CENTER_ALIGNMENT);
871 setAlignmentY(CENTER_ALIGNMENT);
872 setDisplayedMnemonicIndex(-1);
879 * Get the model the button is currently using.
881 * @return The current model
883 public ButtonModel getModel()
889 * Set the model the button is currently using. This un-registers all
890 * listeners associated with the current model, and re-registers them
891 * with the new model.
893 * @param newModel The new model
895 public void setModel(ButtonModel newModel)
897 if (newModel == model)
902 model.removeActionListener(actionListener);
903 model.removeChangeListener(changeListener);
904 model.removeItemListener(itemListener);
906 ButtonModel old = model;
910 model.addActionListener(actionListener);
911 model.addChangeListener(changeListener);
912 model.addItemListener(itemListener);
914 firePropertyChange(MODEL_CHANGED_PROPERTY, old, model);
919 protected void init(String text, Icon icon)
921 // If text is null, we fall back to the empty
922 // string (which is set using AbstractButton's
924 // This way the behavior of the JDK is matched.
933 * <p>Returns the action command string for this button's model.</p>
935 * <p>If the action command was set to <code>null</code>, the button's
936 * text (label) is returned instead.</p>
938 * @return The current action command string from the button's model
940 public String getActionCommand()
942 String ac = model.getActionCommand();
950 * Sets the action command string for this button's model.
952 * @param actionCommand The new action command string to set in the button's
955 public void setActionCommand(String actionCommand)
958 model.setActionCommand(actionCommand);
962 * Adds an ActionListener to the button's listener list. When the
963 * button's model is clicked it fires an ActionEvent, and these
964 * listeners will be called.
966 * @param l The new listener to add
968 public void addActionListener(ActionListener l)
970 listenerList.add(ActionListener.class, l);
974 * Removes an ActionListener from the button's listener list.
976 * @param l The listener to remove
978 public void removeActionListener(ActionListener l)
980 listenerList.remove(ActionListener.class, l);
984 * Returns all added <code>ActionListener</code> objects.
986 * @return an array of listeners
990 public ActionListener[] getActionListeners()
992 return (ActionListener[]) listenerList.getListeners(ActionListener.class);
996 * Adds an ItemListener to the button's listener list. When the button's
997 * model changes state (between any of ARMED, ENABLED, PRESSED, ROLLOVER
998 * or SELECTED) it fires an ItemEvent, and these listeners will be
1001 * @param l The new listener to add
1003 public void addItemListener(ItemListener l)
1005 listenerList.add(ItemListener.class, l);
1009 * Removes an ItemListener from the button's listener list.
1011 * @param l The listener to remove
1013 public void removeItemListener(ItemListener l)
1015 listenerList.remove(ItemListener.class, l);
1019 * Returns all added <code>ItemListener</code> objects.
1021 * @return an array of listeners
1025 public ItemListener[] getItemListeners()
1027 return (ItemListener[]) listenerList.getListeners(ItemListener.class);
1031 * Adds a ChangeListener to the button's listener list. When the button's
1032 * model changes any of its (non-bound) properties, these listeners will be
1035 * @param l The new listener to add
1037 public void addChangeListener(ChangeListener l)
1039 listenerList.add(ChangeListener.class, l);
1043 * Removes a ChangeListener from the button's listener list.
1045 * @param l The listener to remove
1047 public void removeChangeListener(ChangeListener l)
1049 listenerList.remove(ChangeListener.class, l);
1053 * Returns all added <code>ChangeListener</code> objects.
1055 * @return an array of listeners
1059 public ChangeListener[] getChangeListeners()
1061 return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
1065 * Calls {@link ItemListener#itemStateChanged} on each ItemListener in
1066 * the button's listener list.
1068 * @param e The event signifying that the button's model changed state
1070 protected void fireItemStateChanged(ItemEvent e)
1073 ItemListener[] listeners = getItemListeners();
1075 for (int i = 0; i < listeners.length; i++)
1076 listeners[i].itemStateChanged(e);
1080 * Calls {@link ActionListener#actionPerformed} on each {@link
1081 * ActionListener} in the button's listener list.
1083 * @param e The event signifying that the button's model was clicked
1085 protected void fireActionPerformed(ActionEvent e)
1087 // Dispatch a copy of the given ActionEvent in order to
1088 // set the source and action command correctly.
1089 ActionEvent ae = new ActionEvent(
1096 ActionListener[] listeners = getActionListeners();
1098 for (int i = 0; i < listeners.length; i++)
1099 listeners[i].actionPerformed(ae);
1103 * Calls {@link ChangeListener#stateChanged} on each {@link ChangeListener}
1104 * in the button's listener list.
1106 protected void fireStateChanged()
1108 ChangeListener[] listeners = getChangeListeners();
1110 for (int i = 0; i < listeners.length; i++)
1111 listeners[i].stateChanged(changeEvent);
1115 * Get the current keyboard mnemonic value. This value corresponds to a
1116 * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1117 * codes) and is used to activate the button when pressed in conjunction
1118 * with the "mouseless modifier" of the button's look and feel class, and
1119 * when focus is in one of the button's ancestors.
1121 * @return The button's current keyboard mnemonic
1123 public int getMnemonic()
1125 ButtonModel mod = getModel();
1127 return mod.getMnemonic();
1132 * Set the current keyboard mnemonic value. This value corresponds to a
1133 * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1134 * codes) and is used to activate the button when pressed in conjunction
1135 * with the "mouseless modifier" of the button's look and feel class, and
1136 * when focus is in one of the button's ancestors.
1138 * @param mne A new mnemonic to use for the button
1140 public void setMnemonic(char mne)
1142 setMnemonic((int) mne);
1146 * Set the current keyboard mnemonic value. This value corresponds to a
1147 * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1148 * codes) and is used to activate the button when pressed in conjunction
1149 * with the "mouseless modifier" of the button's look and feel class, and
1150 * when focus is in one of the button's ancestors.
1152 * @param mne A new mnemonic to use for the button
1154 public void setMnemonic(int mne)
1156 ButtonModel mod = getModel();
1159 old = mod.getMnemonic();
1164 mod.setMnemonic(mne);
1166 if (text != null && !text.equals(""))
1168 // Since lower case char = upper case char for
1169 // mnemonic, we will convert both text and mnemonic
1170 // to upper case before checking if mnemonic character occurs
1171 // in the menu item text.
1172 int upperCaseMne = Character.toUpperCase((char) mne);
1173 String upperCaseText = text.toUpperCase();
1174 setDisplayedMnemonicIndex(upperCaseText.indexOf(upperCaseMne));
1177 firePropertyChange(MNEMONIC_CHANGED_PROPERTY, old, mne);
1184 * Sets the button's mnemonic index. The mnemonic index is a hint to the
1185 * look and feel class, suggesting which character in the button's label
1186 * should be underlined when drawing the label. If the mnemonic index is
1187 * -1, no mnemonic will be displayed.
1189 * If no mnemonic index is set, the button will choose a mnemonic index
1190 * by default, which will be the first occurrence of the mnemonic
1191 * character in the button's text.
1193 * @param index An offset into the "text" property of the button
1194 * @throws IllegalArgumentException If <code>index</code> is not within the
1195 * range of legal offsets for the "text" property of the button.
1199 public void setDisplayedMnemonicIndex(int index)
1201 if (index < -1 || (text != null && index >= text.length()))
1202 throw new IllegalArgumentException();
1204 mnemonicIndex = index;
1208 * Get the button's mnemonic index, which is an offset into the button's
1209 * "text" property. The character specified by this offset should be
1210 * underlined when the look and feel class draws this button.
1212 * @return An index into the button's "text" property
1214 public int getDisplayedMnemonicIndex()
1216 return mnemonicIndex;
1221 * Set the "rolloverEnabled" property. When rollover is enabled, and the
1222 * look and feel supports it, the button will change its icon to
1223 * rolloverIcon, when the mouse passes over it.
1225 * @param r Whether or not to enable rollover icon changes
1227 public void setRolloverEnabled(boolean r)
1229 clientRolloverEnabledSet = true;
1230 if (rollOverEnabled != r)
1232 rollOverEnabled = r;
1233 firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r);
1240 * Returns whether or not rollover icon changes are enabled on the
1243 * @return The state of the "rolloverEnabled" property
1245 public boolean isRolloverEnabled()
1247 return rollOverEnabled;
1251 * Set the value of the button's "selected" property. Selection is only
1252 * meaningful for toggle-type buttons (check boxes, radio buttons).
1254 * @param s New value for the property
1256 public void setSelected(boolean s)
1258 ButtonModel mod = getModel();
1264 * Get the value of the button's "selected" property. Selection is only
1265 * meaningful for toggle-type buttons (check boxes, radio buttons).
1267 * @return The value of the property
1269 public boolean isSelected()
1271 ButtonModel mod = getModel();
1273 return mod.isSelected();
1278 * Enables or disables the button. A button will neither be selectable
1279 * nor preform any actions unless it is enabled.
1281 * @param b Whether or not to enable the button
1283 public void setEnabled(boolean b)
1285 // Do nothing if state does not change.
1286 if (b == isEnabled())
1288 super.setEnabled(b);
1290 ButtonModel mod = getModel();
1296 * Set the horizontal alignment of the button's text and icon. The
1297 * alignment is a numeric constant from {@link SwingConstants}. It must
1298 * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1299 * <code>LEADING</code> or <code>TRAILING</code>. The default is
1300 * <code>CENTER</code>.
1302 * @return The current horizontal alignment
1304 * @see #setHorizontalAlignment(int)
1306 public int getHorizontalAlignment()
1308 return horizontalAlignment;
1312 * Set the horizontal alignment of the button's text and icon. The
1313 * alignment is a numeric constant from {@link SwingConstants}. It must
1314 * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1315 * <code>LEADING</code> or <code>TRAILING</code>. The default is
1316 * <code>CENTER</code>.
1318 * @param a The new horizontal alignment
1319 * @throws IllegalArgumentException If alignment is not one of the legal
1322 * @see #getHorizontalAlignment()
1324 public void setHorizontalAlignment(int a)
1326 if (horizontalAlignment == a)
1328 if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING
1330 throw new IllegalArgumentException("Invalid alignment.");
1331 int old = horizontalAlignment;
1332 horizontalAlignment = a;
1333 firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1339 * Get the horizontal position of the button's text relative to its
1340 * icon. The position is a numeric constant from {@link
1341 * SwingConstants}. It must be one of: <code>RIGHT</code>,
1342 * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1343 * <code>TRAILING</code>. The default is <code>TRAILING</code>.
1345 * @return The current horizontal text position
1347 public int getHorizontalTextPosition()
1349 return horizontalTextPosition;
1353 * Set the horizontal position of the button's text relative to its
1354 * icon. The position is a numeric constant from {@link
1355 * SwingConstants}. It must be one of: <code>RIGHT</code>,
1356 * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1357 * <code>TRAILING</code>. The default is <code>TRAILING</code>.
1359 * @param t The new horizontal text position
1360 * @throws IllegalArgumentException If position is not one of the legal
1363 public void setHorizontalTextPosition(int t)
1365 if (horizontalTextPosition == t)
1367 if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING
1369 throw new IllegalArgumentException("Invalid alignment.");
1371 int old = horizontalTextPosition;
1372 horizontalTextPosition = t;
1373 firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1379 * Get the vertical alignment of the button's text and icon. The
1380 * alignment is a numeric constant from {@link SwingConstants}. It must
1381 * be one of: <code>CENTER</code>, <code>TOP</code>, or
1382 * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1384 * @return The current vertical alignment
1386 * @see #setVerticalAlignment(int)
1388 public int getVerticalAlignment()
1390 return verticalAlignment;
1394 * Set the vertical alignment of the button's text and icon. The
1395 * alignment is a numeric constant from {@link SwingConstants}. It must
1396 * be one of: <code>CENTER</code>, <code>TOP</code>, or
1397 * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1399 * @param a The new vertical alignment
1400 * @throws IllegalArgumentException If alignment is not one of the legal
1403 * @see #getVerticalAlignment()
1405 public void setVerticalAlignment(int a)
1407 if (verticalAlignment == a)
1409 if (a != TOP && a != CENTER && a != BOTTOM)
1410 throw new IllegalArgumentException("Invalid alignment.");
1412 int old = verticalAlignment;
1413 verticalAlignment = a;
1414 firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1420 * Get the vertical position of the button's text relative to its
1421 * icon. The alignment is a numeric constant from {@link
1422 * SwingConstants}. It must be one of: <code>CENTER</code>,
1423 * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1424 * <code>CENTER</code>.
1426 * @return The current vertical position
1428 public int getVerticalTextPosition()
1430 return verticalTextPosition;
1434 * Set the vertical position of the button's text relative to its
1435 * icon. The alignment is a numeric constant from {@link
1436 * SwingConstants}. It must be one of: <code>CENTER</code>,
1437 * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1438 * <code>CENTER</code>.
1440 * @param t The new vertical position
1441 * @throws IllegalArgumentException If position is not one of the legal
1444 public void setVerticalTextPosition(int t)
1446 if (verticalTextPosition == t)
1448 if (t != TOP && t != CENTER && t != BOTTOM)
1449 throw new IllegalArgumentException("Invalid alignment.");
1451 int old = verticalTextPosition;
1452 verticalTextPosition = t;
1453 firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1459 * Set the value of the "borderPainted" property. If set to
1460 * <code>false</code>, the button's look and feel class should not paint
1461 * a border for the button. The default is <code>true</code>.
1463 * @return The current value of the property.
1465 public boolean isBorderPainted()
1467 return borderPainted;
1471 * Set the value of the "borderPainted" property. If set to
1472 * <code>false</code>, the button's look and feel class should not paint
1473 * a border for the button. The default is <code>true</code>.
1475 * @param b The new value of the property.
1477 public void setBorderPainted(boolean b)
1479 clientBorderPaintedSet = true;
1480 if (borderPainted == b)
1482 boolean old = borderPainted;
1484 firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
1490 * Get the value of the "action" property.
1492 * @return The current value of the "action" property
1494 public Action getAction()
1500 * <p>Set the button's "action" property, subscribing the new action to the
1501 * button, as an ActionListener, if it is not already subscribed. The old
1502 * Action, if it exists, is unsubscribed, and the button is unsubscribed
1503 * from the old Action if it was previously subscribed as a
1504 * PropertyChangeListener.</p>
1506 * <p>This method also configures several of the button's properties from
1507 * the Action, by calling {@link #configurePropertiesFromAction}, and
1508 * subscribes the button to the Action as a PropertyChangeListener.
1509 * Subsequent changes to the Action will thus reconfigure the button
1510 * automatically.</p>
1512 * @param a The new value of the "action" property
1514 public void setAction(Action a)
1518 action.removePropertyChangeListener(actionPropertyChangeListener);
1519 removeActionListener(action);
1520 if (actionPropertyChangeListener != null)
1522 action.removePropertyChangeListener(actionPropertyChangeListener);
1523 actionPropertyChangeListener = null;
1527 Action old = action;
1529 configurePropertiesFromAction(action);
1532 actionPropertyChangeListener = createActionPropertyChangeListener(a);
1533 action.addPropertyChangeListener(actionPropertyChangeListener);
1534 addActionListener(action);
1539 * Return the button's default "icon" property.
1541 * @return The current default icon
1543 public Icon getIcon()
1545 return default_icon;
1549 * Set the button's default "icon" property. This icon is used as a basis
1550 * for the pressed and disabled icons, if none are explicitly set.
1552 * @param i The new default icon
1554 public void setIcon(Icon i)
1556 if (default_icon == i)
1559 Icon old = default_icon;
1561 firePropertyChange(ICON_CHANGED_PROPERTY, old, i);
1567 * Return the button's "text" property. This property is synonymous with
1568 * the "label" property.
1570 * @return The current "text" property
1572 public String getText()
1578 * Set the button's "label" property. This property is synonymous with the
1581 * @param label The new "label" property
1583 * @deprecated use <code>setText(text)</code>
1585 public void setLabel(String label)
1591 * Return the button's "label" property. This property is synonymous with
1592 * the "text" property.
1594 * @return The current "label" property
1596 * @deprecated use <code>getText()</code>
1598 public String getLabel()
1604 * Set the button's "text" property. This property is synonymous with the
1607 * @param t The new "text" property
1609 public void setText(String t)
1616 firePropertyChange(TEXT_CHANGED_PROPERTY, old, t);
1622 * Set the value of the {@link #iconTextGap} property.
1624 * @param i The new value of the property
1628 public void setIconTextGap(int i)
1630 clientIconTextGapSet = true;
1631 if (iconTextGap == i)
1634 int old = iconTextGap;
1636 firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
1642 * Get the value of the {@link #iconTextGap} property.
1644 * @return The current value of the property
1648 public int getIconTextGap()
1654 * Return the button's "margin" property, which is an {@link Insets} object
1655 * describing the distance between the button's border and its text and
1658 * @return The current "margin" property
1660 public Insets getMargin()
1666 * Set the button's "margin" property, which is an {@link Insets} object
1667 * describing the distance between the button's border and its text and
1670 * @param m The new "margin" property
1672 public void setMargin(Insets m)
1677 Insets old = margin;
1679 firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
1685 * Return the button's "pressedIcon" property. The look and feel class
1686 * should paint this icon when the "pressed" property of the button's
1687 * {@link ButtonModel} is <code>true</code>. This property may be
1688 * <code>null</code>, in which case the default icon is used.
1690 * @return The current "pressedIcon" property
1692 public Icon getPressedIcon()
1694 return pressed_icon;
1698 * Set the button's "pressedIcon" property. The look and feel class
1699 * should paint this icon when the "pressed" property of the button's
1700 * {@link ButtonModel} is <code>true</code>. This property may be
1701 * <code>null</code>, in which case the default icon is used.
1703 * @param pressedIcon The new "pressedIcon" property
1705 public void setPressedIcon(Icon pressedIcon)
1707 if (pressed_icon == pressedIcon)
1710 Icon old = pressed_icon;
1711 pressed_icon = pressedIcon;
1712 firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon);
1718 * Return the button's "disabledIcon" property. The look and feel class
1719 * should paint this icon when the "enabled" property of the button's
1720 * {@link ButtonModel} is <code>false</code>. This property may be
1721 * <code>null</code>, in which case an icon is constructed, based on the
1724 * @return The current "disabledIcon" property
1726 public Icon getDisabledIcon()
1728 if (disabledIcon == null && default_icon instanceof ImageIcon)
1730 Image iconImage = ((ImageIcon) default_icon).getImage();
1731 Image grayImage = GrayFilter.createDisabledImage(iconImage);
1732 disabledIcon = new ImageIcon(grayImage);
1735 return disabledIcon;
1739 * Set the button's "disabledIcon" property. The look and feel class should
1740 * paint this icon when the "enabled" property of the button's {@link
1741 * ButtonModel} is <code>false</code>. This property may be
1742 * <code>null</code>, in which case an icon is constructed, based on the
1745 * @param d The new "disabledIcon" property
1747 public void setDisabledIcon(Icon d)
1749 if (disabledIcon == d)
1751 Icon old = disabledIcon;
1753 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d);
1759 * Return the button's "paintFocus" property. This property controls
1760 * whether or not the look and feel class will paint a special indicator
1761 * of focus state for the button. If it is false, the button still paints
1762 * when focused, but no special decoration is painted to indicate the
1763 * presence of focus.
1765 * @return The current "paintFocus" property
1767 public boolean isFocusPainted()
1769 return focusPainted;
1773 * Set the button's "paintFocus" property. This property controls whether
1774 * or not the look and feel class will paint a special indicator of focus
1775 * state for the button. If it is false, the button still paints when
1776 * focused, but no special decoration is painted to indicate the presence
1779 * @param p The new "paintFocus" property
1781 public void setFocusPainted(boolean p)
1783 if (focusPainted == p)
1786 boolean old = focusPainted;
1788 firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p);
1794 * Verifies that a particular key is one of the valid constants used for
1795 * describing horizontal alignment and positioning. The valid constants
1796 * are the following members of {@link SwingConstants}:
1797 * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1798 * <code>LEADING</code> or <code>TRAILING</code>.
1800 * @param key The key to check
1801 * @param exception A message to include in an IllegalArgumentException
1803 * @return the value of key
1805 * @throws IllegalArgumentException If key is not one of the valid constants
1807 * @see #setHorizontalTextPosition(int)
1808 * @see #setHorizontalAlignment(int)
1810 protected int checkHorizontalKey(int key, String exception)
1814 case SwingConstants.RIGHT:
1815 case SwingConstants.LEFT:
1816 case SwingConstants.CENTER:
1817 case SwingConstants.LEADING:
1818 case SwingConstants.TRAILING:
1821 throw new IllegalArgumentException(exception);
1827 * Verifies that a particular key is one of the valid constants used for
1828 * describing vertical alignment and positioning. The valid constants are
1829 * the following members of {@link SwingConstants}: <code>TOP</code>,
1830 * <code>BOTTOM</code> or <code>CENTER</code>.
1832 * @param key The key to check
1833 * @param exception A message to include in an IllegalArgumentException
1835 * @return the value of key
1837 * @throws IllegalArgumentException If key is not one of the valid constants
1839 * @see #setVerticalTextPosition(int)
1840 * @see #setVerticalAlignment(int)
1842 protected int checkVerticalKey(int key, String exception)
1846 case SwingConstants.TOP:
1847 case SwingConstants.BOTTOM:
1848 case SwingConstants.CENTER:
1851 throw new IllegalArgumentException(exception);
1857 * Configure various properties of the button by reading properties
1858 * of an {@link Action}. The mapping of properties is as follows:
1862 * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr>
1864 * <tr><td>NAME </td> <td>text </td></tr>
1865 * <tr><td>SMALL_ICON </td> <td>icon </td></tr>
1866 * <tr><td>SHORT_DESCRIPTION </td> <td>toolTipText </td></tr>
1867 * <tr><td>MNEMONIC_KEY </td> <td>mnemonic </td></tr>
1868 * <tr><td>ACTION_COMMAND_KEY </td> <td>actionCommand </td></tr>
1872 * <p>In addition, this method always sets the button's "enabled" property to
1873 * the value of the Action's "enabled" property.</p>
1875 * <p>If the provided Action is <code>null</code>, the text, icon, and
1876 * toolTipText properties of the button are set to <code>null</code>, and
1877 * the "enabled" property is set to <code>true</code>; the mnemonic and
1878 * actionCommand properties are unchanged.</p>
1880 * @param a An Action to configure the button from
1882 protected void configurePropertiesFromAction(Action a)
1889 setToolTipText(null);
1893 setText((String) (a.getValue(Action.NAME)));
1894 setIcon((Icon) (a.getValue(Action.SMALL_ICON)));
1895 setEnabled(a.isEnabled());
1896 setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
1897 if (a.getValue(Action.MNEMONIC_KEY) != null)
1898 setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue());
1899 String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY));
1901 // Set actionCommand to button's text by default if it is not specified
1902 if (actionCommand != null)
1903 setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY)));
1905 setActionCommand(getText());
1910 * <p>A factory method which should return an {@link ActionListener} that
1911 * propagates events from the button's {@link ButtonModel} to any of the
1912 * button's ActionListeners. By default, this is an inner class which
1913 * calls {@link AbstractButton#fireActionPerformed} with a modified copy
1914 * of the incoming model {@link ActionEvent}.</p>
1916 * <p>The button calls this method during construction, stores the
1917 * resulting ActionListener in its <code>actionListener</code> member
1918 * field, and subscribes it to the button's model. If the button's model
1919 * is changed, this listener is unsubscribed from the old model and
1920 * subscribed to the new one.</p>
1922 * @return A new ActionListener
1924 protected ActionListener createActionListener()
1926 return new ActionListener()
1928 public void actionPerformed(ActionEvent e)
1930 AbstractButton.this.fireActionPerformed(e);
1936 * <p>A factory method which should return a {@link PropertyChangeListener}
1937 * that accepts changes to the specified {@link Action} and reconfigure
1938 * the {@link AbstractButton}, by default using the {@link
1939 * #configurePropertiesFromAction} method.</p>
1941 * <p>The button calls this method whenever a new Action is assigned to
1942 * the button's "action" property, via {@link #setAction}, and stores the
1943 * resulting PropertyChangeListener in its
1944 * <code>actionPropertyChangeListener</code> member field. The button
1945 * then subscribes the listener to the button's new action. If the
1946 * button's action is changed subsequently, the listener is unsubscribed
1947 * from the old action and subscribed to the new one.</p>
1949 * @param a The Action which will be listened to, and which should be
1950 * the same as the source of any PropertyChangeEvents received by the
1951 * new listener returned from this method.
1953 * @return A new PropertyChangeListener
1955 protected PropertyChangeListener createActionPropertyChangeListener(Action a)
1957 return new PropertyChangeListener()
1959 public void propertyChange(PropertyChangeEvent e)
1961 Action act = (Action) (e.getSource());
1962 if (e.getPropertyName().equals("enabled"))
1963 setEnabled(act.isEnabled());
1964 else if (e.getPropertyName().equals(Action.NAME))
1965 setText((String) (act.getValue(Action.NAME)));
1966 else if (e.getPropertyName().equals(Action.SMALL_ICON))
1967 setIcon((Icon) (act.getValue(Action.SMALL_ICON)));
1968 else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION))
1969 setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION)));
1970 else if (e.getPropertyName().equals(Action.MNEMONIC_KEY))
1971 if (act.getValue(Action.MNEMONIC_KEY) != null)
1972 setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY)))
1974 else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY))
1975 setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY)));
1981 * <p>Factory method which creates a {@link ChangeListener}, used to
1982 * subscribe to ChangeEvents from the button's model. Subclasses of
1983 * AbstractButton may wish to override the listener used to subscribe to
1984 * such ChangeEvents. By default, the listener just propagates the
1985 * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
1986 * AbstractButton#fireStateChanged} method.</p>
1988 * <p>The button calls this method during construction, stores the
1989 * resulting ChangeListener in its <code>changeListener</code> member
1990 * field, and subscribes it to the button's model. If the button's model
1991 * is changed, this listener is unsubscribed from the old model and
1992 * subscribed to the new one.</p>
1994 * @return The new ChangeListener
1996 protected ChangeListener createChangeListener()
1998 return new ButtonChangeListener();
2002 * <p>Factory method which creates a {@link ItemListener}, used to
2003 * subscribe to ItemEvents from the button's model. Subclasses of
2004 * AbstractButton may wish to override the listener used to subscribe to
2005 * such ItemEvents. By default, the listener just propagates the
2006 * {@link ItemEvent} to the button's ItemListeners, via the {@link
2007 * AbstractButton#fireItemStateChanged} method.</p>
2009 * <p>The button calls this method during construction, stores the
2010 * resulting ItemListener in its <code>changeListener</code> member
2011 * field, and subscribes it to the button's model. If the button's model
2012 * is changed, this listener is unsubscribed from the old model and
2013 * subscribed to the new one.</p>
2015 * <p>Note that ItemEvents are only generated from the button's model
2016 * when the model's <em>selected</em> property changes. If you want to
2017 * subscribe to other properties of the model, you must subscribe to
2020 * @return The new ItemListener
2022 protected ItemListener createItemListener()
2024 return new ItemListener()
2026 public void itemStateChanged(ItemEvent e)
2028 AbstractButton.this.fireItemStateChanged(e);
2034 * Programmatically perform a "click" on the button: arming, pressing,
2035 * waiting, un-pressing, and disarming the model.
2037 public void doClick()
2043 * Programmatically perform a "click" on the button: arming, pressing,
2044 * waiting, un-pressing, and disarming the model.
2046 * @param pressTime The number of milliseconds to wait in the pressed state
2048 public void doClick(int pressTime)
2050 ButtonModel mod = getModel();
2054 mod.setPressed(true);
2057 java.lang.Thread.sleep(pressTime);
2059 catch (java.lang.InterruptedException e)
2061 // probably harmless
2063 mod.setPressed(false);
2064 mod.setArmed(false);
2069 * Return the button's disabled selected icon. The look and feel class
2070 * should paint this icon when the "enabled" property of the button's model
2071 * is <code>false</code> and its "selected" property is
2072 * <code>true</code>. This icon can be <code>null</code>, in which case
2073 * it is synthesized from the button's selected icon.
2075 * @return The current disabled selected icon
2077 public Icon getDisabledSelectedIcon()
2079 return disabledSelectedIcon;
2083 * Set the button's disabled selected icon. The look and feel class
2084 * should paint this icon when the "enabled" property of the button's model
2085 * is <code>false</code> and its "selected" property is
2086 * <code>true</code>. This icon can be <code>null</code>, in which case
2087 * it is synthesized from the button's selected icon.
2089 * @param icon The new disabled selected icon
2091 public void setDisabledSelectedIcon(Icon icon)
2093 if (disabledSelectedIcon == icon)
2096 Icon old = disabledSelectedIcon;
2097 disabledSelectedIcon = icon;
2098 firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
2104 * Return the button's rollover icon. The look and feel class should
2105 * paint this icon when the "rolloverEnabled" property of the button is
2106 * <code>true</code> and the mouse rolls over the button.
2108 * @return The current rollover icon
2110 public Icon getRolloverIcon()
2112 return rolloverIcon;
2116 * Set the button's rollover icon and sets the <code>rolloverEnabled</code>
2117 * property to <code>true</code>. The look and feel class should
2118 * paint this icon when the "rolloverEnabled" property of the button is
2119 * <code>true</code> and the mouse rolls over the button.
2121 * @param r The new rollover icon
2123 public void setRolloverIcon(Icon r)
2125 if (rolloverIcon == r)
2128 Icon old = rolloverIcon;
2130 firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
2131 setRolloverEnabled(true);
2137 * Return the button's rollover selected icon. The look and feel class
2138 * should paint this icon when the "rolloverEnabled" property of the button
2139 * is <code>true</code>, the "selected" property of the button's model is
2140 * <code>true</code>, and the mouse rolls over the button.
2142 * @return The current rollover selected icon
2144 public Icon getRolloverSelectedIcon()
2146 return rolloverSelectedIcon;
2150 * Set the button's rollover selected icon and sets the
2151 * <code>rolloverEnabled</code> property to <code>true</code>. The look and
2152 * feel class should paint this icon when the "rolloverEnabled" property of
2153 * the button is <code>true</code>, the "selected" property of the button's
2154 * model is <code>true</code>, and the mouse rolls over the button.
2156 * @param r The new rollover selected icon.
2158 public void setRolloverSelectedIcon(Icon r)
2160 if (rolloverSelectedIcon == r)
2163 Icon old = rolloverSelectedIcon;
2164 rolloverSelectedIcon = r;
2165 firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
2166 setRolloverEnabled(true);
2172 * Return the button's selected icon. The look and feel class should
2173 * paint this icon when the "selected" property of the button's model is
2174 * <code>true</code>, and either the "rolloverEnabled" property of the
2175 * button is <code>false</code> or the mouse is not currently rolled
2178 * @return The current selected icon
2180 public Icon getSelectedIcon()
2182 return selectedIcon;
2186 * Set the button's selected icon. The look and feel class should
2187 * paint this icon when the "selected" property of the button's model is
2188 * <code>true</code>, and either the "rolloverEnabled" property of the
2189 * button is <code>false</code> or the mouse is not currently rolled
2192 * @param s The new selected icon
2194 public void setSelectedIcon(Icon s)
2196 if (selectedIcon == s)
2199 Icon old = selectedIcon;
2201 firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
2207 * Returns an single-element array containing the "text" property of the
2208 * button if the "selected" property of the button's model is
2209 * <code>true</code>, otherwise returns <code>null</code>.
2211 * @return The button's "selected object" array
2213 public Object[] getSelectedObjects()
2217 Object[] objs = new Object[1];
2218 objs[0] = getText();
2228 * Called when image data becomes available for one of the button's icons.
2230 * @param img The image being updated
2231 * @param infoflags One of the constant codes in {@link ImageObserver} used
2232 * to describe updated portions of an image.
2233 * @param x X coordinate of the region being updated
2234 * @param y Y coordinate of the region being updated
2235 * @param w Width of the region beign updated
2236 * @param h Height of the region being updated
2238 * @return <code>true</code> if img is equal to the button's current icon,
2239 * otherwise <code>false</code>
2241 public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
2244 return current_icon == img;
2248 * Returns the value of the button's "contentAreaFilled" property. This
2249 * property indicates whether the area surrounding the text and icon of
2250 * the button should be filled by the look and feel class. If this
2251 * property is <code>false</code>, the look and feel class should leave
2252 * the content area transparent.
2254 * @return The current value of the "contentAreaFilled" property
2256 public boolean isContentAreaFilled()
2258 return contentAreaFilled;
2262 * Sets the value of the button's "contentAreaFilled" property. This
2263 * property indicates whether the area surrounding the text and icon of
2264 * the button should be filled by the look and feel class. If this
2265 * property is <code>false</code>, the look and feel class should leave
2266 * the content area transparent.
2268 * @param b The new value of the "contentAreaFilled" property
2270 public void setContentAreaFilled(boolean b)
2272 clientContentAreaFilledSet = true;
2273 if (contentAreaFilled == b)
2276 // The JDK sets the opaque property to the value of the contentAreaFilled
2277 // property, so should we do.
2279 boolean old = contentAreaFilled;
2280 contentAreaFilled = b;
2281 firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
2285 * Paints the button's border, if the button's "borderPainted" property is
2286 * <code>true</code>, by out calling to the button's look and feel class.
2288 * @param g The graphics context used to paint the border
2290 protected void paintBorder(Graphics g)
2292 if (isBorderPainted())
2293 super.paintBorder(g);
2297 * Returns a string, used only for debugging, which identifies or somehow
2298 * represents this button. The exact value is implementation-defined.
2300 * @return A string representation of the button
2302 protected String paramString()
2304 StringBuffer sb = new StringBuffer();
2305 sb.append(super.paramString());
2306 sb.append(",defaultIcon=");
2307 if (getIcon() != null)
2308 sb.append(getIcon());
2309 sb.append(",disabledIcon=");
2310 if (getDisabledIcon() != null)
2311 sb.append(getDisabledIcon());
2312 sb.append(",disabledSelectedIcon=");
2313 if (getDisabledSelectedIcon() != null)
2314 sb.append(getDisabledSelectedIcon());
2315 sb.append(",margin=");
2316 if (getMargin() != null)
2317 sb.append(getMargin());
2318 sb.append(",paintBorder=").append(isBorderPainted());
2319 sb.append(",paintFocus=").append(isFocusPainted());
2320 sb.append(",pressedIcon=");
2321 if (getPressedIcon() != null)
2322 sb.append(getPressedIcon());
2323 sb.append(",rolloverEnabled=").append(isRolloverEnabled());
2324 sb.append(",rolloverIcon=");
2325 if (getRolloverIcon() != null)
2326 sb.append(getRolloverIcon());
2327 sb.append(",rolloverSelected=");
2328 if (getRolloverSelectedIcon() != null)
2329 sb.append(getRolloverSelectedIcon());
2330 sb.append(",selectedIcon=");
2331 if (getSelectedIcon() != null)
2332 sb.append(getSelectedIcon());
2333 sb.append(",text=");
2334 if (getText() != null)
2335 sb.append(getText());
2336 return sb.toString();
2340 * Set the "UI" property of the button, which is a look and feel class
2341 * responsible for handling the button's input events and painting it.
2343 * @param ui The new "UI" property
2345 public void setUI(ButtonUI ui)
2351 * Set the "UI" property of the button, which is a look and feel class
2352 * responsible for handling the button's input events and painting it.
2354 * @return The current "UI" property
2356 public ButtonUI getUI()
2358 return (ButtonUI) ui;
2362 * Set the "UI" property to a class constructed, via the {@link
2363 * UIManager}, from the current look and feel. This should be overridden
2364 * for each subclass of AbstractButton, to retrieve a suitable {@link
2365 * ButtonUI} look and feel class.
2367 public void updateUI()
2369 // TODO: What to do here?
2373 * Returns the current time in milliseconds in which clicks gets coalesced
2374 * into a single <code>ActionEvent</code>.
2376 * @return the time in milliseconds
2380 public long getMultiClickThreshhold()
2382 return multiClickThreshhold;
2386 * Sets the time in milliseconds in which clicks gets coalesced into a single
2387 * <code>ActionEvent</code>.
2389 * @param threshhold the time in milliseconds
2393 public void setMultiClickThreshhold(long threshhold)
2396 throw new IllegalArgumentException();
2398 multiClickThreshhold = threshhold;
2402 * Adds the specified component to this AbstractButton. This overrides the
2403 * default in order to install an {@link OverlayLayout} layout manager
2404 * before adding the component. The layout manager is only installed if
2405 * no other layout manager has been installed before.
2407 * @param comp the component to be added
2408 * @param constraints constraints for the layout manager
2409 * @param index the index at which the component is added
2413 protected void addImpl(Component comp, Object constraints, int index)
2415 // We use a client property here, so that no extra memory is used in
2416 // the common case with no layout manager.
2417 if (getClientProperty("AbstractButton.customLayoutSet") == null)
2418 setLayout(new OverlayLayout(this));
2419 super.addImpl(comp, constraints, index);
2423 * Sets a layout manager on this AbstractButton. This is overridden in order
2424 * to detect if the application sets a custom layout manager. If no custom
2425 * layout manager is set, {@link #addImpl(Component, Object, int)} installs
2426 * an OverlayLayout before adding a component.
2428 * @param layout the layout manager to install
2432 public void setLayout(LayoutManager layout)
2434 // We use a client property here, so that no extra memory is used in
2435 // the common case with no layout manager.
2436 putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE);
2437 super.setLayout(layout);
2442 * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
2444 * @param propertyName the name of the property
2445 * @param value the value of the property
2447 * @throws IllegalArgumentException if the specified property cannot be set
2449 * @throws ClassCastException if the property value does not match the
2451 * @throws NullPointerException if <code>c</code> or
2452 * <code>propertyValue</code> is <code>null</code>
2454 void setUIProperty(String propertyName, Object value)
2456 if (propertyName.equals("borderPainted"))
2458 if (! clientBorderPaintedSet)
2460 setBorderPainted(((Boolean) value).booleanValue());
2461 clientBorderPaintedSet = false;
2464 else if (propertyName.equals("rolloverEnabled"))
2466 if (! clientRolloverEnabledSet)
2468 setRolloverEnabled(((Boolean) value).booleanValue());
2469 clientRolloverEnabledSet = false;
2472 else if (propertyName.equals("iconTextGap"))
2474 if (! clientIconTextGapSet)
2476 setIconTextGap(((Integer) value).intValue());
2477 clientIconTextGapSet = false;
2480 else if (propertyName.equals("contentAreaFilled"))
2482 if (! clientContentAreaFilledSet)
2484 setContentAreaFilled(((Boolean) value).booleanValue());
2485 clientContentAreaFilledSet = false;
2490 super.setUIProperty(propertyName, value);