Merge from mainline.
[official-gcc.git] / libjava / classpath / javax / swing / AbstractButton.java
blob348daece174e4f12ee58057547009aa1daff4c03
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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. */
38 package javax.swing;
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;
81 /**
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
92 * are available:</p>
94 * <table>
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>
125 * </table>
127 * <p>The various behavioral aspects of these properties follows:</p>
129 * <ul>
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>
156 * </ul>
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();
191 repaint();
195 /** The icon displayed by default. */
196 Icon default_icon;
198 /** The icon displayed when the button is pressed. */
199 Icon pressed_icon;
201 /** The icon displayed when the button is disabled. */
202 Icon disabeldIcon;
204 /** The icon displayed when the button is selected. */
205 Icon selectedIcon;
207 /** The icon displayed when the button is selected but disabled. */
208 Icon disabledSelectedIcon;
210 /** The icon displayed when the button is rolled over. */
211 Icon rolloverIcon;
213 /** The icon displayed when the button is selected and rolled over. */
214 Icon rolloverSelectedIcon;
216 /** The icon currently displayed. */
217 Icon current_icon;
219 /** The text displayed in the button. */
220 String text;
223 * The gap between icon and text, if both icon and text are
224 * non-<code>null</code>.
226 int iconTextGap;
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. */
253 Action action;
255 /** The button's current state. */
256 protected ButtonModel model;
258 /** The margin between the button's border and its label. */
259 Insets margin;
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.
265 int mnemonicIndex;
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
284 * Action.
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
334 * changes.
336 public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY =
337 "contentAreaFilled";
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
346 * changes.
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
358 * changes.
360 public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY =
361 "horizontalAlignment";
364 * Fired in a PropertyChangeEvent when the "horizontalTextPosition" property
365 * changes.
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
388 * changes.
390 public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY =
391 "rolloverEnabled";
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
400 * changes.
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
415 * changes.
417 public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY =
418 "verticalAlignment";
421 * Fired in a PropertyChangeEvent when the "verticalTextPosition" property
422 * changes.
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,
432 AccessibleText
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);
458 if (isSelected())
459 state.add(AccessibleState.CHECKED);
461 return state;
465 * Returns the accessible name for the button.
467 public String getAccessibleName()
469 String result = super.getAccessibleName();
470 if (result == null)
471 result = text;
472 return result;
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 };
495 return ret;
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();
513 if (group != null)
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);
526 relations.add(rel);
529 return relations;
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()
540 return this;
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()
552 return this;
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()
564 return 1;
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)
578 String descr = null;
579 if (actionIndex == 0)
581 // FIXME: Supply localized descriptions in the UIDefaults.
582 descr = UIManager.getString("AbstractButton.clickText");
584 return descr;
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)
603 doClick();
604 retVal = true;
606 return retVal;
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()
618 Integer retVal;
619 if (isSelected())
620 retVal = new Integer(1);
621 else
622 retVal = new Integer(0);
623 return retVal;
627 * Sets the current accessible value as object. If the specified number
628 * is 0 the button will be deselected, otherwise the button will
629 * be selected.
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>
634 * otherwise
636 public boolean setCurrentAccessibleValue(Number value)
638 boolean retVal = false;
639 if (value != null)
641 if (value.intValue() == 0)
642 setSelected(false);
643 else
644 setSelected(true);
645 retVal = true;
647 return retVal;
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
677 * <code>this</code>.
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
693 * HTML labels.
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)
701 int index = -1;
702 View view = (View) getClientProperty(BasicHTML.propertyKey);
703 if (view != null)
705 Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
706 index = view.viewToModel(p.x, p.y, shape, new Position.Bias[1]);
708 return index;
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
718 * button's label
720 public Rectangle getCharacterBounds(int i)
722 Rectangle rect = null;
723 View view = (View) getClientProperty(BasicHTML.propertyKey);
724 if (view != null)
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)
734 rect = null;
737 return rect;
741 * Returns the number of characters in the button's label.
743 * @return the bounds of the character at the specified index of the
744 * button's label
746 public int getCharCount()
748 int charCount;
749 View view = (View) getClientProperty(BasicHTML.propertyKey);
750 if (view != null)
752 charCount = view.getDocument().getLength();
754 else
756 charCount = getAccessibleName().length();
758 return charCount;
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()
768 return -1;
771 public String getAtIndex(int value0, int value1)
772 throws NotImplementedException
774 return null; // TODO
777 public String getAfterIndex(int value0, int value1)
778 throws NotImplementedException
780 return null; // TODO
783 public String getBeforeIndex(int value0, int value1)
784 throws NotImplementedException
786 return null; // TODO
790 * Returns the text attribute for the character at the specified character
791 * index.
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);
802 if (view != null)
806 return atts;
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()
817 return -1;
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()
828 return -1;
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()
839 return null;
844 * Creates a new AbstractButton object. Subclasses should call the following
845 * sequence in their constructor in order to initialize the button correctly:
846 * <pre>
847 * super();
848 * init(text, icon);
849 * </pre>
851 * The {@link #init(String, Icon)} method is not called automatically by this
852 * constructor.
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;
868 focusPainted = true;
869 setFocusable(true);
870 setAlignmentX(CENTER_ALIGNMENT);
871 setAlignmentY(CENTER_ALIGNMENT);
872 setDisplayedMnemonicIndex(-1);
873 setOpaque(true);
874 text = "";
875 updateUI();
879 * Get the model the button is currently using.
881 * @return The current model
883 public ButtonModel getModel()
885 return model;
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)
898 return;
900 if (model != null)
902 model.removeActionListener(actionListener);
903 model.removeChangeListener(changeListener);
904 model.removeItemListener(itemListener);
906 ButtonModel old = model;
907 model = newModel;
908 if (model != null)
910 model.addActionListener(actionListener);
911 model.addChangeListener(changeListener);
912 model.addItemListener(itemListener);
914 firePropertyChange(MODEL_CHANGED_PROPERTY, old, model);
915 revalidate();
916 repaint();
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
923 // constructor).
924 // This way the behavior of the JDK is matched.
925 if(text != null)
926 this.text = text;
928 if (icon != null)
929 default_icon = icon;
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();
943 if (ac != null)
944 return ac;
945 else
946 return text;
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
953 * model.
955 public void setActionCommand(String actionCommand)
957 if (model != null)
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
988 * @since 1.4
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
999 * called.
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
1023 * @since 1.4
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
1033 * called.
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
1057 * @since 1.4
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)
1072 e.setSource(this);
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(
1090 this,
1091 e.getID(),
1092 getActionCommand(),
1093 e.getWhen(),
1094 e.getModifiers());
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();
1126 if (mod != null)
1127 return mod.getMnemonic();
1128 return -1;
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();
1157 int old = -1;
1158 if (mod != null)
1159 old = mod.getMnemonic();
1161 if (old != mne)
1163 if (mod != null)
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);
1178 revalidate();
1179 repaint();
1183 /**
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.
1196 * @since 1.4
1199 public void setDisplayedMnemonicIndex(int index)
1201 if (index < -1 || (text != null && index >= text.length()))
1202 throw new IllegalArgumentException();
1204 mnemonicIndex = index;
1207 /**
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);
1234 revalidate();
1235 repaint();
1240 * Returns whether or not rollover icon changes are enabled on the
1241 * button.
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();
1259 if (mod != null)
1260 mod.setSelected(s);
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();
1272 if (mod != null)
1273 return mod.isSelected();
1274 return false;
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())
1287 return;
1288 super.setEnabled(b);
1289 setFocusable(b);
1290 ButtonModel mod = getModel();
1291 if (mod != null)
1292 mod.setEnabled(b);
1295 /**
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
1318 * constants.
1320 public void setHorizontalAlignment(int a)
1322 if (horizontalAlignment == a)
1323 return;
1325 int old = horizontalAlignment;
1326 horizontalAlignment = a;
1327 firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1328 revalidate();
1329 repaint();
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
1355 * constants.
1357 public void setHorizontalTextPosition(int t)
1359 if (horizontalTextPosition == t)
1360 return;
1362 int old = horizontalTextPosition;
1363 horizontalTextPosition = t;
1364 firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1365 revalidate();
1366 repaint();
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
1390 * constants.
1392 public void setVerticalAlignment(int a)
1394 if (verticalAlignment == a)
1395 return;
1397 int old = verticalAlignment;
1398 verticalAlignment = a;
1399 firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1400 revalidate();
1401 repaint();
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
1427 * constants.
1429 public void setVerticalTextPosition(int t)
1431 if (verticalTextPosition == t)
1432 return;
1434 int old = verticalTextPosition;
1435 verticalTextPosition = t;
1436 firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1437 revalidate();
1438 repaint();
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)
1464 return;
1465 boolean old = borderPainted;
1466 borderPainted = b;
1467 firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
1468 revalidate();
1469 repaint();
1473 * Get the value of the "action" property.
1475 * @return The current value of the "action" property
1477 public Action getAction()
1479 return action;
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)
1499 if (action != null)
1501 action.removePropertyChangeListener(actionPropertyChangeListener);
1502 removeActionListener(action);
1503 if (actionPropertyChangeListener != null)
1505 action.removePropertyChangeListener(actionPropertyChangeListener);
1506 actionPropertyChangeListener = null;
1510 Action old = action;
1511 action = a;
1512 configurePropertiesFromAction(action);
1513 if (action != null)
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)
1540 return;
1542 Icon old = default_icon;
1543 default_icon = i;
1544 firePropertyChange(ICON_CHANGED_PROPERTY, old, i);
1545 revalidate();
1546 repaint();
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()
1557 return text;
1561 * Set the button's "label" property. This property is synonymous with the
1562 * "text" property.
1564 * @param label The new "label" property
1566 * @deprecated use <code>setText(text)</code>
1568 public void setLabel(String label)
1570 setText(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()
1583 return getText();
1587 * Set the button's "text" property. This property is synonymous with the
1588 * "label" property.
1590 * @param t The new "text" property
1592 public void setText(String t)
1594 if (text == t)
1595 return;
1597 String old = text;
1598 text = t;
1599 firePropertyChange(TEXT_CHANGED_PROPERTY, old, t);
1600 revalidate();
1601 repaint();
1605 * Set the value of the {@link #iconTextGap} property.
1607 * @param i The new value of the property
1609 * @since 1.4
1611 public void setIconTextGap(int i)
1613 clientIconTextGapSet = true;
1614 if (iconTextGap == i)
1615 return;
1617 int old = iconTextGap;
1618 iconTextGap = i;
1619 firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
1620 revalidate();
1621 repaint();
1625 * Get the value of the {@link #iconTextGap} property.
1627 * @return The current value of the property
1629 * @since 1.4
1631 public int getIconTextGap()
1633 return iconTextGap;
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
1639 * icon.
1641 * @return The current "margin" property
1643 public Insets getMargin()
1645 return margin;
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
1651 * icon.
1653 * @param m The new "margin" property
1655 public void setMargin(Insets m)
1657 if (margin == m)
1658 return;
1660 Insets old = margin;
1661 margin = m;
1662 firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
1663 revalidate();
1664 repaint();
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)
1691 return;
1693 Icon old = pressed_icon;
1694 pressed_icon = pressedIcon;
1695 firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon);
1696 revalidate();
1697 repaint();
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
1705 * default icon.
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
1726 * default icon.
1728 * @param d The new "disabledIcon" property
1730 public void setDisabledIcon(Icon d)
1732 disabeldIcon = d;
1733 revalidate();
1734 repaint();
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
1756 * of focus.
1758 * @param p The new "paintFocus" property
1760 public void setFocusPainted(boolean p)
1762 if (focusPainted == p)
1763 return;
1765 boolean old = focusPainted;
1766 focusPainted = p;
1767 firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p);
1768 revalidate();
1769 repaint();
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)
1791 switch (key)
1793 case SwingConstants.RIGHT:
1794 case SwingConstants.LEFT:
1795 case SwingConstants.CENTER:
1796 case SwingConstants.LEADING:
1797 case SwingConstants.TRAILING:
1798 break;
1799 default:
1800 throw new IllegalArgumentException(exception);
1802 return key;
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)
1823 switch (key)
1825 case SwingConstants.TOP:
1826 case SwingConstants.BOTTOM:
1827 case SwingConstants.CENTER:
1828 break;
1829 default:
1830 throw new IllegalArgumentException(exception);
1832 return key;
1836 * Configure various properties of the button by reading properties
1837 * of an {@link Action}. The mapping of properties is as follows:
1839 * <table>
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>
1849 * </table>
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)
1863 if (a == null)
1865 setText(null);
1866 setIcon(null);
1867 setEnabled(true);
1868 setToolTipText(null);
1870 else
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)));
1883 else
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)))
1952 .intValue());
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
1997 * ChangeEvents.
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()
2018 doClick(100);
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();
2030 if (mod != null)
2032 mod.setArmed(true);
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)
2073 return;
2075 Icon old = disabledSelectedIcon;
2076 disabledSelectedIcon = icon;
2077 firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
2078 revalidate();
2079 repaint();
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)
2104 return;
2106 Icon old = rolloverIcon;
2107 rolloverIcon = r;
2108 firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
2109 revalidate();
2110 repaint();
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)
2137 return;
2139 Icon old = rolloverSelectedIcon;
2140 rolloverSelectedIcon = r;
2141 firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
2142 revalidate();
2143 repaint();
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
2151 * over the button.
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
2165 * over the button.
2167 * @param s The new selected icon
2169 public void setSelectedIcon(Icon s)
2171 if (selectedIcon == s)
2172 return;
2174 Icon old = selectedIcon;
2175 selectedIcon = s;
2176 firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
2177 revalidate();
2178 repaint();
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()
2190 if (isSelected())
2192 Object[] objs = new Object[1];
2193 objs[0] = getText();
2194 return objs;
2196 else
2198 return null;
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,
2217 int h)
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)
2249 return;
2251 // The JDK sets the opaque property to the value of the contentAreaFilled
2252 // property, so should we do.
2253 setOpaque(b);
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)
2322 super.setUI(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
2353 * @since 1.4
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
2366 * @since 1.4
2368 public void setMultiClickThreshhold(long threshhold)
2370 if (threshhold < 0)
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
2386 * @since 1.5
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
2405 * @since 1.5
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);
2416 * Helper method for
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
2423 * by this method
2424 * @throws ClassCastException if the property value does not match the
2425 * property type
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;
2463 else
2465 super.setUIProperty(propertyName, value);