1 /* AbstractButton.java -- Provides basic button functionality.
2 Copyright (C) 2002, 2004 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 miliseconds 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 curren 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 maxinimum accessible value for the AccessibleAbstractButton,
655 * which is <code>1</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>RIGHT</code>.
1302 * @return The current horizontal alignment
1304 public int getHorizontalAlignment()
1306 return horizontalAlignment
;
1310 * Set the horizontal alignment of the button's text and icon. The
1311 * alignment is a numeric constant from {@link SwingConstants}. It must
1312 * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1313 * <code>LEADING</code> or <code>TRAILING</code>. The default is
1314 * <code>RIGHT</code>.
1316 * @param a The new horizontal alignment
1317 * @throws IllegalArgumentException If alignment is not one of the legal
1320 public void setHorizontalAlignment(int a
)
1322 if (horizontalAlignment
== a
)
1325 int old
= horizontalAlignment
;
1326 horizontalAlignment
= a
;
1327 firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY
, old
, a
);
1333 * Get the horizontal position of the button's text relative to its
1334 * icon. The position is a numeric constant from {@link
1335 * SwingConstants}. It must be one of: <code>RIGHT</code>,
1336 * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1337 * <code>TRAILING</code>. The default is <code>TRAILING</code>.
1339 * @return The current horizontal text position
1341 public int getHorizontalTextPosition()
1343 return horizontalTextPosition
;
1347 * Set the horizontal position of the button's text relative to its
1348 * icon. The position is a numeric constant from {@link
1349 * SwingConstants}. It must be one of: <code>RIGHT</code>,
1350 * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1351 * <code>TRAILING</code>. The default is <code>TRAILING</code>.
1353 * @param t The new horizontal text position
1354 * @throws IllegalArgumentException If position is not one of the legal
1357 public void setHorizontalTextPosition(int t
)
1359 if (horizontalTextPosition
== t
)
1362 int old
= horizontalTextPosition
;
1363 horizontalTextPosition
= t
;
1364 firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY
, old
, t
);
1370 * Get the vertical alignment of the button's text and icon. The
1371 * alignment is a numeric constant from {@link SwingConstants}. It must
1372 * be one of: <code>CENTER</code>, <code>TOP</code>, or
1373 * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1375 * @return The current vertical alignment
1377 public int getVerticalAlignment()
1379 return verticalAlignment
;
1383 * Set the vertical alignment of the button's text and icon. The
1384 * alignment is a numeric constant from {@link SwingConstants}. It must
1385 * be one of: <code>CENTER</code>, <code>TOP</code>, or
1386 * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1388 * @param a The new vertical alignment
1389 * @throws IllegalArgumentException If alignment is not one of the legal
1392 public void setVerticalAlignment(int a
)
1394 if (verticalAlignment
== a
)
1397 int old
= verticalAlignment
;
1398 verticalAlignment
= a
;
1399 firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY
, old
, a
);
1405 * Get the vertical position of the button's text relative to its
1406 * icon. The alignment is a numeric constant from {@link
1407 * SwingConstants}. It must be one of: <code>CENTER</code>,
1408 * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1409 * <code>CENTER</code>.
1411 * @return The current vertical position
1413 public int getVerticalTextPosition()
1415 return verticalTextPosition
;
1419 * Set the vertical position of the button's text relative to its
1420 * icon. The alignment is a numeric constant from {@link
1421 * SwingConstants}. It must be one of: <code>CENTER</code>,
1422 * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1423 * <code>CENTER</code>.
1425 * @param t The new vertical position
1426 * @throws IllegalArgumentException If position is not one of the legal
1429 public void setVerticalTextPosition(int t
)
1431 if (verticalTextPosition
== t
)
1434 int old
= verticalTextPosition
;
1435 verticalTextPosition
= t
;
1436 firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY
, old
, t
);
1442 * Set the value of the "borderPainted" property. If set to
1443 * <code>false</code>, the button's look and feel class should not paint
1444 * a border for the button. The default is <code>true</code>.
1446 * @return The current value of the property.
1448 public boolean isBorderPainted()
1450 return borderPainted
;
1454 * Set the value of the "borderPainted" property. If set to
1455 * <code>false</code>, the button's look and feel class should not paint
1456 * a border for the button. The default is <code>true</code>.
1458 * @param b The new value of the property.
1460 public void setBorderPainted(boolean b
)
1462 clientBorderPaintedSet
= true;
1463 if (borderPainted
== b
)
1465 boolean old
= borderPainted
;
1467 firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY
, old
, b
);
1473 * Get the value of the "action" property.
1475 * @return The current value of the "action" property
1477 public Action
getAction()
1483 * <p>Set the button's "action" property, subscribing the new action to the
1484 * button, as an ActionListener, if it is not already subscribed. The old
1485 * Action, if it exists, is unsubscribed, and the button is unsubscribed
1486 * from the old Action if it was previously subscribed as a
1487 * PropertyChangeListener.</p>
1489 * <p>This method also configures several of the button's properties from
1490 * the Action, by calling {@link #configurePropertiesFromAction}, and
1491 * subscribes the button to the Action as a PropertyChangeListener.
1492 * Subsequent changes to the Action will thus reconfigure the button
1493 * automatically.</p>
1495 * @param a The new value of the "action" property
1497 public void setAction(Action a
)
1501 action
.removePropertyChangeListener(actionPropertyChangeListener
);
1502 removeActionListener(action
);
1503 if (actionPropertyChangeListener
!= null)
1505 action
.removePropertyChangeListener(actionPropertyChangeListener
);
1506 actionPropertyChangeListener
= null;
1510 Action old
= action
;
1512 configurePropertiesFromAction(action
);
1515 actionPropertyChangeListener
= createActionPropertyChangeListener(a
);
1516 action
.addPropertyChangeListener(actionPropertyChangeListener
);
1517 addActionListener(action
);
1522 * Return the button's default "icon" property.
1524 * @return The current default icon
1526 public Icon
getIcon()
1528 return default_icon
;
1532 * Set the button's default "icon" property. This icon is used as a basis
1533 * for the pressed and disabled icons, if none are explicitly set.
1535 * @param i The new default icon
1537 public void setIcon(Icon i
)
1539 if (default_icon
== i
)
1542 Icon old
= default_icon
;
1544 firePropertyChange(ICON_CHANGED_PROPERTY
, old
, i
);
1550 * Return the button's "text" property. This property is synonymous with
1551 * the "label" property.
1553 * @return The current "text" property
1555 public String
getText()
1561 * Set the button's "label" property. This property is synonymous with the
1564 * @param label The new "label" property
1566 * @deprecated use <code>setText(text)</code>
1568 public void setLabel(String label
)
1574 * Return the button's "label" property. This property is synonymous with
1575 * the "text" property.
1577 * @return The current "label" property
1579 * @deprecated use <code>getText()</code>
1581 public String
getLabel()
1587 * Set the button's "text" property. This property is synonymous with the
1590 * @param t The new "text" property
1592 public void setText(String t
)
1599 firePropertyChange(TEXT_CHANGED_PROPERTY
, old
, t
);
1605 * Set the value of the {@link #iconTextGap} property.
1607 * @param i The new value of the property
1611 public void setIconTextGap(int i
)
1613 clientIconTextGapSet
= true;
1614 if (iconTextGap
== i
)
1617 int old
= iconTextGap
;
1619 firePropertyChange("iconTextGap", new Integer(old
), new Integer(i
));
1625 * Get the value of the {@link #iconTextGap} property.
1627 * @return The current value of the property
1631 public int getIconTextGap()
1637 * Return the button's "margin" property, which is an {@link Insets} object
1638 * describing the distance between the button's border and its text and
1641 * @return The current "margin" property
1643 public Insets
getMargin()
1649 * Set the button's "margin" property, which is an {@link Insets} object
1650 * describing the distance between the button's border and its text and
1653 * @param m The new "margin" property
1655 public void setMargin(Insets m
)
1660 Insets old
= margin
;
1662 firePropertyChange(MARGIN_CHANGED_PROPERTY
, old
, m
);
1668 * Return the button's "pressedIcon" property. The look and feel class
1669 * should paint this icon when the "pressed" property of the button's
1670 * {@link ButtonModel} is <code>true</code>. This property may be
1671 * <code>null</code>, in which case the default icon is used.
1673 * @return The current "pressedIcon" property
1675 public Icon
getPressedIcon()
1677 return pressed_icon
;
1681 * Set the button's "pressedIcon" property. The look and feel class
1682 * should paint this icon when the "pressed" property of the button's
1683 * {@link ButtonModel} is <code>true</code>. This property may be
1684 * <code>null</code>, in which case the default icon is used.
1686 * @param pressedIcon The new "pressedIcon" property
1688 public void setPressedIcon(Icon pressedIcon
)
1690 if (pressed_icon
== pressedIcon
)
1693 Icon old
= pressed_icon
;
1694 pressed_icon
= pressedIcon
;
1695 firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY
, old
, pressed_icon
);
1701 * Return the button's "disabledIcon" property. The look and feel class
1702 * should paint this icon when the "enabled" property of the button's
1703 * {@link ButtonModel} is <code>false</code>. This property may be
1704 * <code>null</code>, in which case an icon is constructed, based on the
1707 * @return The current "disabledIcon" property
1709 public Icon
getDisabledIcon()
1711 if (disabeldIcon
== null && default_icon
instanceof ImageIcon
)
1713 Image iconImage
= ((ImageIcon
) default_icon
).getImage();
1714 Image grayImage
= GrayFilter
.createDisabledImage(iconImage
);
1715 disabeldIcon
= new ImageIcon(grayImage
);
1718 return disabeldIcon
;
1722 * Set the button's "disabledIcon" property. The look and feel class should
1723 * paint this icon when the "enabled" property of the button's {@link
1724 * ButtonModel} is <code>false</code>. This property may be
1725 * <code>null</code>, in which case an icon is constructed, based on the
1728 * @param d The new "disabledIcon" property
1730 public void setDisabledIcon(Icon d
)
1738 * Return the button's "paintFocus" property. This property controls
1739 * whether or not the look and feel class will paint a special indicator
1740 * of focus state for the button. If it is false, the button still paints
1741 * when focused, but no special decoration is painted to indicate the
1742 * presence of focus.
1744 * @return The current "paintFocus" property
1746 public boolean isFocusPainted()
1748 return focusPainted
;
1752 * Set the button's "paintFocus" property. This property controls whether
1753 * or not the look and feel class will paint a special indicator of focus
1754 * state for the button. If it is false, the button still paints when
1755 * focused, but no special decoration is painted to indicate the presence
1758 * @param p The new "paintFocus" property
1760 public void setFocusPainted(boolean p
)
1762 if (focusPainted
== p
)
1765 boolean old
= focusPainted
;
1767 firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY
, old
, p
);
1773 * Verifies that a particular key is one of the valid constants used for
1774 * describing horizontal alignment and positioning. The valid constants
1775 * are the following members of {@link SwingConstants}:
1776 * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1777 * <code>LEADING</code> or <code>TRAILING</code>.
1779 * @param key The key to check
1780 * @param exception A message to include in an IllegalArgumentException
1782 * @return the value of key
1784 * @throws IllegalArgumentException If key is not one of the valid constants
1786 * @see #setHorizontalTextPosition(int)
1787 * @see #setHorizontalAlignment(int)
1789 protected int checkHorizontalKey(int key
, String exception
)
1793 case SwingConstants
.RIGHT
:
1794 case SwingConstants
.LEFT
:
1795 case SwingConstants
.CENTER
:
1796 case SwingConstants
.LEADING
:
1797 case SwingConstants
.TRAILING
:
1800 throw new IllegalArgumentException(exception
);
1806 * Verifies that a particular key is one of the valid constants used for
1807 * describing vertical alignment and positioning. The valid constants are
1808 * the following members of {@link SwingConstants}: <code>TOP</code>,
1809 * <code>BOTTOM</code> or <code>CENTER</code>.
1811 * @param key The key to check
1812 * @param exception A message to include in an IllegalArgumentException
1814 * @return the value of key
1816 * @throws IllegalArgumentException If key is not one of the valid constants
1818 * @see #setVerticalTextPosition(int)
1819 * @see #setVerticalAlignment(int)
1821 protected int checkVerticalKey(int key
, String exception
)
1825 case SwingConstants
.TOP
:
1826 case SwingConstants
.BOTTOM
:
1827 case SwingConstants
.CENTER
:
1830 throw new IllegalArgumentException(exception
);
1836 * Configure various properties of the button by reading properties
1837 * of an {@link Action}. The mapping of properties is as follows:
1841 * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr>
1843 * <tr><td>NAME </td> <td>text </td></tr>
1844 * <tr><td>SMALL_ICON </td> <td>icon </td></tr>
1845 * <tr><td>SHORT_DESCRIPTION </td> <td>toolTipText </td></tr>
1846 * <tr><td>MNEMONIC_KEY </td> <td>mnemonic </td></tr>
1847 * <tr><td>ACTION_COMMAND_KEY </td> <td>actionCommand </td></tr>
1851 * <p>In addition, this method always sets the button's "enabled" property to
1852 * the value of the Action's "enabled" property.</p>
1854 * <p>If the provided Action is <code>null</code>, the text, icon, and
1855 * toolTipText properties of the button are set to <code>null</code>, and
1856 * the "enabled" property is set to <code>true</code>; the mnemonic and
1857 * actionCommand properties are unchanged.</p>
1859 * @param a An Action to configure the button from
1861 protected void configurePropertiesFromAction(Action a
)
1868 setToolTipText(null);
1872 setText((String
) (a
.getValue(Action
.NAME
)));
1873 setIcon((Icon
) (a
.getValue(Action
.SMALL_ICON
)));
1874 setEnabled(a
.isEnabled());
1875 setToolTipText((String
) (a
.getValue(Action
.SHORT_DESCRIPTION
)));
1876 if (a
.getValue(Action
.MNEMONIC_KEY
) != null)
1877 setMnemonic(((Integer
) (a
.getValue(Action
.MNEMONIC_KEY
))).intValue());
1878 String actionCommand
= (String
) (a
.getValue(Action
.ACTION_COMMAND_KEY
));
1880 // Set actionCommand to button's text by default if it is not specified
1881 if (actionCommand
!= null)
1882 setActionCommand((String
) (a
.getValue(Action
.ACTION_COMMAND_KEY
)));
1884 setActionCommand(getText());
1889 * <p>A factory method which should return an {@link ActionListener} that
1890 * propagates events from the button's {@link ButtonModel} to any of the
1891 * button's ActionListeners. By default, this is an inner class which
1892 * calls {@link AbstractButton#fireActionPerformed} with a modified copy
1893 * of the incoming model {@link ActionEvent}.</p>
1895 * <p>The button calls this method during construction, stores the
1896 * resulting ActionListener in its <code>actionListener</code> member
1897 * field, and subscribes it to the button's model. If the button's model
1898 * is changed, this listener is unsubscribed from the old model and
1899 * subscribed to the new one.</p>
1901 * @return A new ActionListener
1903 protected ActionListener
createActionListener()
1905 return new ActionListener()
1907 public void actionPerformed(ActionEvent e
)
1909 AbstractButton
.this.fireActionPerformed(e
);
1915 * <p>A factory method which should return a {@link PropertyChangeListener}
1916 * that accepts changes to the specified {@link Action} and reconfigure
1917 * the {@link AbstractButton}, by default using the {@link
1918 * #configurePropertiesFromAction} method.</p>
1920 * <p>The button calls this method whenever a new Action is assigned to
1921 * the button's "action" property, via {@link #setAction}, and stores the
1922 * resulting PropertyChangeListener in its
1923 * <code>actionPropertyChangeListener</code> member field. The button
1924 * then subscribes the listener to the button's new action. If the
1925 * button's action is changed subsequently, the listener is unsubscribed
1926 * from the old action and subscribed to the new one.</p>
1928 * @param a The Action which will be listened to, and which should be
1929 * the same as the source of any PropertyChangeEvents received by the
1930 * new listener returned from this method.
1932 * @return A new PropertyChangeListener
1934 protected PropertyChangeListener
createActionPropertyChangeListener(Action a
)
1936 return new PropertyChangeListener()
1938 public void propertyChange(PropertyChangeEvent e
)
1940 Action act
= (Action
) (e
.getSource());
1941 if (e
.getPropertyName().equals("enabled"))
1942 setEnabled(act
.isEnabled());
1943 else if (e
.getPropertyName().equals(Action
.NAME
))
1944 setText((String
) (act
.getValue(Action
.NAME
)));
1945 else if (e
.getPropertyName().equals(Action
.SMALL_ICON
))
1946 setIcon((Icon
) (act
.getValue(Action
.SMALL_ICON
)));
1947 else if (e
.getPropertyName().equals(Action
.SHORT_DESCRIPTION
))
1948 setToolTipText((String
) (act
.getValue(Action
.SHORT_DESCRIPTION
)));
1949 else if (e
.getPropertyName().equals(Action
.MNEMONIC_KEY
))
1950 if (act
.getValue(Action
.MNEMONIC_KEY
) != null)
1951 setMnemonic(((Integer
) (act
.getValue(Action
.MNEMONIC_KEY
)))
1953 else if (e
.getPropertyName().equals(Action
.ACTION_COMMAND_KEY
))
1954 setActionCommand((String
) (act
.getValue(Action
.ACTION_COMMAND_KEY
)));
1960 * <p>Factory method which creates a {@link ChangeListener}, used to
1961 * subscribe to ChangeEvents from the button's model. Subclasses of
1962 * AbstractButton may wish to override the listener used to subscribe to
1963 * such ChangeEvents. By default, the listener just propagates the
1964 * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
1965 * AbstractButton#fireStateChanged} method.</p>
1967 * <p>The button calls this method during construction, stores the
1968 * resulting ChangeListener in its <code>changeListener</code> member
1969 * field, and subscribes it to the button's model. If the button's model
1970 * is changed, this listener is unsubscribed from the old model and
1971 * subscribed to the new one.</p>
1973 * @return The new ChangeListener
1975 protected ChangeListener
createChangeListener()
1977 return new ButtonChangeListener();
1981 * <p>Factory method which creates a {@link ItemListener}, used to
1982 * subscribe to ItemEvents from the button's model. Subclasses of
1983 * AbstractButton may wish to override the listener used to subscribe to
1984 * such ItemEvents. By default, the listener just propagates the
1985 * {@link ItemEvent} to the button's ItemListeners, via the {@link
1986 * AbstractButton#fireItemStateChanged} method.</p>
1988 * <p>The button calls this method during construction, stores the
1989 * resulting ItemListener 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 * <p>Note that ItemEvents are only generated from the button's model
1995 * when the model's <em>selected</em> property changes. If you want to
1996 * subscribe to other properties of the model, you must subscribe to
1999 * @return The new ItemListener
2001 protected ItemListener
createItemListener()
2003 return new ItemListener()
2005 public void itemStateChanged(ItemEvent e
)
2007 AbstractButton
.this.fireItemStateChanged(e
);
2013 * Programmatically perform a "click" on the button: arming, pressing,
2014 * waiting, un-pressing, and disarming the model.
2016 public void doClick()
2022 * Programmatically perform a "click" on the button: arming, pressing,
2023 * waiting, un-pressing, and disarming the model.
2025 * @param pressTime The number of milliseconds to wait in the pressed state
2027 public void doClick(int pressTime
)
2029 ButtonModel mod
= getModel();
2033 mod
.setPressed(true);
2036 java
.lang
.Thread
.sleep(pressTime
);
2038 catch (java
.lang
.InterruptedException e
)
2040 // probably harmless
2042 mod
.setPressed(false);
2043 mod
.setArmed(false);
2048 * Return the button's disabled selected icon. The look and feel class
2049 * should paint this icon when the "enabled" property of the button's model
2050 * is <code>false</code> and its "selected" property is
2051 * <code>true</code>. This icon can be <code>null</code>, in which case
2052 * it is synthesized from the button's selected icon.
2054 * @return The current disabled selected icon
2056 public Icon
getDisabledSelectedIcon()
2058 return disabledSelectedIcon
;
2062 * Set the button's disabled selected icon. The look and feel class
2063 * should paint this icon when the "enabled" property of the button's model
2064 * is <code>false</code> and its "selected" property is
2065 * <code>true</code>. This icon can be <code>null</code>, in which case
2066 * it is synthesized from the button's selected icon.
2068 * @param icon The new disabled selected icon
2070 public void setDisabledSelectedIcon(Icon icon
)
2072 if (disabledSelectedIcon
== icon
)
2075 Icon old
= disabledSelectedIcon
;
2076 disabledSelectedIcon
= icon
;
2077 firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY
, old
, icon
);
2083 * Return the button's rollover icon. The look and feel class should
2084 * paint this icon when the "rolloverEnabled" property of the button is
2085 * <code>true</code> and the mouse rolls over the button.
2087 * @return The current rollover icon
2089 public Icon
getRolloverIcon()
2091 return rolloverIcon
;
2095 * Set the button's rollover icon. The look and feel class should
2096 * paint this icon when the "rolloverEnabled" property of the button is
2097 * <code>true</code> and the mouse rolls over the button.
2099 * @param r The new rollover icon
2101 public void setRolloverIcon(Icon r
)
2103 if (rolloverIcon
== r
)
2106 Icon old
= rolloverIcon
;
2108 firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY
, old
, rolloverIcon
);
2114 * Return the button's rollover selected icon. The look and feel class
2115 * should paint this icon when the "rolloverEnabled" property of the button
2116 * is <code>true</code>, the "selected" property of the button's model is
2117 * <code>true</code>, and the mouse rolls over the button.
2119 * @return The current rollover selected icon
2121 public Icon
getRolloverSelectedIcon()
2123 return rolloverSelectedIcon
;
2127 * Set the button's rollover selected icon. The look and feel class
2128 * should paint this icon when the "rolloverEnabled" property of the button
2129 * is <code>true</code>, the "selected" property of the button's model is
2130 * <code>true</code>, and the mouse rolls over the button.
2132 * @param r The new rollover selected icon
2134 public void setRolloverSelectedIcon(Icon r
)
2136 if (rolloverSelectedIcon
== r
)
2139 Icon old
= rolloverSelectedIcon
;
2140 rolloverSelectedIcon
= r
;
2141 firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY
, old
, r
);
2147 * Return the button's selected icon. The look and feel class should
2148 * paint this icon when the "selected" property of the button's model is
2149 * <code>true</code>, and either the "rolloverEnabled" property of the
2150 * button is <code>false</code> or the mouse is not currently rolled
2153 * @return The current selected icon
2155 public Icon
getSelectedIcon()
2157 return selectedIcon
;
2161 * Set the button's selected icon. The look and feel class should
2162 * paint this icon when the "selected" property of the button's model is
2163 * <code>true</code>, and either the "rolloverEnabled" property of the
2164 * button is <code>false</code> or the mouse is not currently rolled
2167 * @param s The new selected icon
2169 public void setSelectedIcon(Icon s
)
2171 if (selectedIcon
== s
)
2174 Icon old
= selectedIcon
;
2176 firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY
, old
, s
);
2182 * Returns an single-element array containing the "text" property of the
2183 * button if the "selected" property of the button's model is
2184 * <code>true</code>, otherwise returns <code>null</code>.
2186 * @return The button's "selected object" array
2188 public Object
[] getSelectedObjects()
2192 Object
[] objs
= new Object
[1];
2193 objs
[0] = getText();
2203 * Called when image data becomes available for one of the button's icons.
2205 * @param img The image being updated
2206 * @param infoflags One of the constant codes in {@link ImageObserver} used
2207 * to describe updated portions of an image.
2208 * @param x X coordinate of the region being updated
2209 * @param y Y coordinate of the region being updated
2210 * @param w Width of the region beign updated
2211 * @param h Height of the region being updated
2213 * @return <code>true</code> if img is equal to the button's current icon,
2214 * otherwise <code>false</code>
2216 public boolean imageUpdate(Image img
, int infoflags
, int x
, int y
, int w
,
2219 return current_icon
== img
;
2223 * Returns the value of the button's "contentAreaFilled" property. This
2224 * property indicates whether the area surrounding the text and icon of
2225 * the button should be filled by the look and feel class. If this
2226 * property is <code>false</code>, the look and feel class should leave
2227 * the content area transparent.
2229 * @return The current value of the "contentAreaFilled" property
2231 public boolean isContentAreaFilled()
2233 return contentAreaFilled
;
2237 * Sets the value of the button's "contentAreaFilled" property. This
2238 * property indicates whether the area surrounding the text and icon of
2239 * the button should be filled by the look and feel class. If this
2240 * property is <code>false</code>, the look and feel class should leave
2241 * the content area transparent.
2243 * @param b The new value of the "contentAreaFilled" property
2245 public void setContentAreaFilled(boolean b
)
2247 clientContentAreaFilledSet
= true;
2248 if (contentAreaFilled
== b
)
2251 // The JDK sets the opaque property to the value of the contentAreaFilled
2252 // property, so should we do.
2254 boolean old
= contentAreaFilled
;
2255 contentAreaFilled
= b
;
2256 firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY
, old
, b
);
2260 * Paints the button's border, if the button's "borderPainted" property is
2261 * <code>true</code>, by out calling to the button's look and feel class.
2263 * @param g The graphics context used to paint the border
2265 protected void paintBorder(Graphics g
)
2267 if (isBorderPainted())
2268 super.paintBorder(g
);
2272 * Returns a string, used only for debugging, which identifies or somehow
2273 * represents this button. The exact value is implementation-defined.
2275 * @return A string representation of the button
2277 protected String
paramString()
2279 StringBuffer sb
= new StringBuffer();
2280 sb
.append(super.paramString());
2281 sb
.append(",defaultIcon=");
2282 if (getIcon() != null)
2283 sb
.append(getIcon());
2284 sb
.append(",disabledIcon=");
2285 if (getDisabledIcon() != null)
2286 sb
.append(getDisabledIcon());
2287 sb
.append(",disabledSelectedIcon=");
2288 if (getDisabledSelectedIcon() != null)
2289 sb
.append(getDisabledSelectedIcon());
2290 sb
.append(",margin=");
2291 if (getMargin() != null)
2292 sb
.append(getMargin());
2293 sb
.append(",paintBorder=").append(isBorderPainted());
2294 sb
.append(",paintFocus=").append(isFocusPainted());
2295 sb
.append(",pressedIcon=");
2296 if (getPressedIcon() != null)
2297 sb
.append(getPressedIcon());
2298 sb
.append(",rolloverEnabled=").append(isRolloverEnabled());
2299 sb
.append(",rolloverIcon=");
2300 if (getRolloverIcon() != null)
2301 sb
.append(getRolloverIcon());
2302 sb
.append(",rolloverSelected=");
2303 if (getRolloverSelectedIcon() != null)
2304 sb
.append(getRolloverSelectedIcon());
2305 sb
.append(",selectedIcon=");
2306 if (getSelectedIcon() != null)
2307 sb
.append(getSelectedIcon());
2308 sb
.append(",text=");
2309 if (getText() != null)
2310 sb
.append(getText());
2311 return sb
.toString();
2315 * Set the "UI" property of the button, which is a look and feel class
2316 * responsible for handling the button's input events and painting it.
2318 * @param ui The new "UI" property
2320 public void setUI(ButtonUI ui
)
2326 * Set the "UI" property of the button, which is a look and feel class
2327 * responsible for handling the button's input events and painting it.
2329 * @return The current "UI" property
2331 public ButtonUI
getUI()
2333 return (ButtonUI
) ui
;
2337 * Set the "UI" property to a class constructed, via the {@link
2338 * UIManager}, from the current look and feel. This should be overridden
2339 * for each subclass of AbstractButton, to retrieve a suitable {@link
2340 * ButtonUI} look and feel class.
2342 public void updateUI()
2344 // TODO: What to do here?
2348 * Returns the current time in milliseconds in which clicks gets coalesced
2349 * into a single <code>ActionEvent</code>.
2351 * @return the time in milliseconds
2355 public long getMultiClickThreshhold()
2357 return multiClickThreshhold
;
2361 * Sets the time in milliseconds in which clicks gets coalesced into a single
2362 * <code>ActionEvent</code>.
2364 * @param threshhold the time in milliseconds
2368 public void setMultiClickThreshhold(long threshhold
)
2371 throw new IllegalArgumentException();
2373 multiClickThreshhold
= threshhold
;
2377 * Adds the specified component to this AbstractButton. This overrides the
2378 * default in order to install an {@link OverlayLayout} layout manager
2379 * before adding the component. The layout manager is only installed if
2380 * no other layout manager has been installed before.
2382 * @param comp the component to be added
2383 * @param constraints constraints for the layout manager
2384 * @param index the index at which the component is added
2388 protected void addImpl(Component comp
, Object constraints
, int index
)
2390 // We use a client property here, so that no extra memory is used in
2391 // the common case with no layout manager.
2392 if (getClientProperty("AbstractButton.customLayoutSet") == null)
2393 setLayout(new OverlayLayout(this));
2394 super.addImpl(comp
, constraints
, index
);
2398 * Sets a layout manager on this AbstractButton. This is overridden in order
2399 * to detect if the application sets a custom layout manager. If no custom
2400 * layout manager is set, {@link #addImpl(Component, Object, int)} installs
2401 * an OverlayLayout before adding a component.
2403 * @param layout the layout manager to install
2407 public void setLayout(LayoutManager layout
)
2409 // We use a client property here, so that no extra memory is used in
2410 // the common case with no layout manager.
2411 putClientProperty("AbstractButton.customLayoutSet", Boolean
.TRUE
);
2412 super.setLayout(layout
);
2417 * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
2419 * @param propertyName the name of the property
2420 * @param value the value of the property
2422 * @throws IllegalArgumentException if the specified property cannot be set
2424 * @throws ClassCastException if the property value does not match the
2426 * @throws NullPointerException if <code>c</code> or
2427 * <code>propertyValue</code> is <code>null</code>
2429 void setUIProperty(String propertyName
, Object value
)
2431 if (propertyName
.equals("borderPainted"))
2433 if (! clientBorderPaintedSet
)
2435 setBorderPainted(((Boolean
) value
).booleanValue());
2436 clientBorderPaintedSet
= false;
2439 else if (propertyName
.equals("rolloverEnabled"))
2441 if (! clientRolloverEnabledSet
)
2443 setRolloverEnabled(((Boolean
) value
).booleanValue());
2444 clientRolloverEnabledSet
= false;
2447 else if (propertyName
.equals("iconTextGap"))
2449 if (! clientIconTextGapSet
)
2451 setIconTextGap(((Integer
) value
).intValue());
2452 clientIconTextGapSet
= false;
2455 else if (propertyName
.equals("contentAreaFilled"))
2457 if (! clientContentAreaFilledSet
)
2459 setContentAreaFilled(((Boolean
) value
).booleanValue());
2460 clientContentAreaFilledSet
= false;
2465 super.setUIProperty(propertyName
, value
);