1 /*
2 * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package javax.swing;
26
27 import java.awt;
28 import java.awt.event;
29 import java.awt.image;
30 import java.text;
31 import java.awt.geom;
32 import java.beans.PropertyChangeEvent;
33 import java.beans.PropertyChangeListener;
34 import java.beans.Transient;
35 import java.util.Enumeration;
36 import java.util.Vector;
37 import java.io.Serializable;
38 import javax.swing.event;
39 import javax.swing.border;
40 import javax.swing.plaf;
41 import javax.accessibility;
42 import javax.swing.text;
43 import javax.swing.text.html;
44 import javax.swing.plaf.basic;
45 import java.util;
46
47 /**
48 * Defines common behaviors for buttons and menu items.
49 * <p>
50 * Buttons can be configured, and to some degree controlled, by
51 * <code><a href="Action.html">Action</a></code>s. Using an
52 * <code>Action</code> with a button has many benefits beyond directly
53 * configuring a button. Refer to <a href="Action.html#buttonActions">
54 * Swing Components Supporting <code>Action</code></a> for more
55 * details, and you can find more information in <a
56 * href="http://java.sun.com/docs/books/tutorial/uiswing/misc/action.html">How
57 * to Use Actions</a>, a section in <em>The Java Tutorial</em>.
58 * <p>
59 * For further information see
60 * <a
61 href="http://java.sun.com/docs/books/tutorial/uiswing/components/button.html">How to Use Buttons, Check Boxes, and Radio Buttons</a>,
62 * a section in <em>The Java Tutorial</em>.
63 * <p>
64 * <strong>Warning:</strong>
65 * Serialized objects of this class will not be compatible with
66 * future Swing releases. The current serialization support is
67 * appropriate for short term storage or RMI between applications running
68 * the same version of Swing. As of 1.4, support for long term storage
69 * of all JavaBeans<sup><font size="-2">TM</font></sup>
70 * has been added to the <code>java.beans</code> package.
71 * Please see {@link java.beans.XMLEncoder}.
72 *
73 * @author Jeff Dinkins
74 */
75 public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {
76
77 // *********************************
78 // ******* Button properties *******
79 // *********************************
80
81 /** Identifies a change in the button model. */
82 public static final String MODEL_CHANGED_PROPERTY = "model";
83 /** Identifies a change in the button's text. */
84 public static final String TEXT_CHANGED_PROPERTY = "text";
85 /** Identifies a change to the button's mnemonic. */
86 public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
87
88 // Text positioning and alignment
89 /** Identifies a change in the button's margins. */
90 public static final String MARGIN_CHANGED_PROPERTY = "margin";
91 /** Identifies a change in the button's vertical alignment. */
92 public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = "verticalAlignment";
93 /** Identifies a change in the button's horizontal alignment. */
94 public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = "horizontalAlignment";
95
96 /** Identifies a change in the button's vertical text position. */
97 public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = "verticalTextPosition";
98 /** Identifies a change in the button's horizontal text position. */
99 public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = "horizontalTextPosition";
100
101 // Paint options
102 /**
103 * Identifies a change to having the border drawn,
104 * or having it not drawn.
105 */
106 public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
107 /**
108 * Identifies a change to having the border highlighted when focused,
109 * or not.
110 */
111 public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
112 /**
113 * Identifies a change from rollover enabled to disabled or back
114 * to enabled.
115 */
116 public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = "rolloverEnabled";
117 /**
118 * Identifies a change to having the button paint the content area.
119 */
120 public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = "contentAreaFilled";
121
122 // Icons
123 /** Identifies a change to the icon that represents the button. */
124 public static final String ICON_CHANGED_PROPERTY = "icon";
125
126 /**
127 * Identifies a change to the icon used when the button has been
128 * pressed.
129 */
130 public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
131 /**
132 * Identifies a change to the icon used when the button has
133 * been selected.
134 */
135 public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
136
137 /**
138 * Identifies a change to the icon used when the cursor is over
139 * the button.
140 */
141 public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
142 /**
143 * Identifies a change to the icon used when the cursor is
144 * over the button and it has been selected.
145 */
146 public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = "rolloverSelectedIcon";
147
148 /**
149 * Identifies a change to the icon used when the button has
150 * been disabled.
151 */
152 public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
153 /**
154 * Identifies a change to the icon used when the button has been
155 * disabled and selected.
156 */
157 public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = "disabledSelectedIcon";
158
159
160 /** The data model that determines the button's state. */
161 protected ButtonModel model = null;
162
163 private String text = ""; // for BeanBox
164 private Insets margin = null;
165 private Insets defaultMargin = null;
166
167 // Button icons
168 // PENDING(jeff) - hold icons in an array
169 private Icon defaultIcon = null;
170 private Icon pressedIcon = null;
171 private Icon disabledIcon = null;
172
173 private Icon selectedIcon = null;
174 private Icon disabledSelectedIcon = null;
175
176 private Icon rolloverIcon = null;
177 private Icon rolloverSelectedIcon = null;
178
179 // Display properties
180 private boolean paintBorder = true;
181 private boolean paintFocus = true;
182 private boolean rolloverEnabled = false;
183 private boolean contentAreaFilled = true;
184
185 // Icon/Label Alignment
186 private int verticalAlignment = CENTER;
187 private int horizontalAlignment = CENTER;
188
189 private int verticalTextPosition = CENTER;
190 private int horizontalTextPosition = TRAILING;
191
192 private int iconTextGap = 4;
193
194 private int mnemonic;
195 private int mnemonicIndex = -1;
196
197 private long multiClickThreshhold = 0;
198
199 private boolean borderPaintedSet = false;
200 private boolean rolloverEnabledSet = false;
201 private boolean iconTextGapSet = false;
202 private boolean contentAreaFilledSet = false;
203
204 // Whether or not we've set the LayoutManager.
205 private boolean setLayout = false;
206
207 // This is only used by JButton, promoted to avoid an extra
208 // boolean field in JButton
209 boolean defaultCapable = true;
210
211 /**
212 * Combined listeners: ActionListener, ChangeListener, ItemListener.
213 */
214 private Handler handler;
215
216 /**
217 * The button model's <code>changeListener</code>.
218 */
219 protected ChangeListener changeListener = null;
220 /**
221 * The button model's <code>ActionListener</code>.
222 */
223 protected ActionListener actionListener = null;
224 /**
225 * The button model's <code>ItemListener</code>.
226 */
227 protected ItemListener itemListener = null;
228
229 /**
230 * Only one <code>ChangeEvent</code> is needed per button
231 * instance since the
232 * event's only state is the source property. The source of events
233 * generated is always "this".
234 */
235 protected transient ChangeEvent changeEvent;
236
237 private boolean hideActionText = false;
238
239 /**
240 * Sets the <code>hideActionText</code> property, which determines
241 * whether the button displays text from the <code>Action</code>.
242 * This is useful only if an <code>Action</code> has been
243 * installed on the button.
244 *
245 * @param hideActionText <code>true</code> if the button's
246 * <code>text</code> property should not reflect
247 * that of the <code>Action</code>; the default is
248 * <code>false</code>
249 * @see <a href="Action.html#buttonActions">Swing Components Supporting
250 * <code>Action</code></a>
251 * @since 1.6
252 * @beaninfo
253 * bound: true
254 * expert: true
255 * description: Whether the text of the button should come from
256 * the <code>Action</code>.
257 */
258 public void setHideActionText(boolean hideActionText) {
259 if (hideActionText != this.hideActionText) {
260 this.hideActionText = hideActionText;
261 if (getAction() != null) {
262 setTextFromAction(getAction(), false);
263 }
264 firePropertyChange("hideActionText", !hideActionText,
265 hideActionText);
266 }
267 }
268
269 /**
270 * Returns the value of the <code>hideActionText</code> property, which
271 * determines whether the button displays text from the
272 * <code>Action</code>. This is useful only if an <code>Action</code>
273 * has been installed on the button.
274 *
275 * @return <code>true</code> if the button's <code>text</code>
276 * property should not reflect that of the
277 * <code>Action</code>; the default is <code>false</code>
278 * @since 1.6
279 */
280 public boolean getHideActionText() {
281 return hideActionText;
282 }
283
284 /**
285 * Returns the button's text.
286 * @return the buttons text
287 * @see #setText
288 */
289 public String getText() {
290 return text;
291 }
292
293 /**
294 * Sets the button's text.
295 * @param text the string used to set the text
296 * @see #getText
297 * @beaninfo
298 * bound: true
299 * preferred: true
300 * attribute: visualUpdate true
301 * description: The button's text.
302 */
303 public void setText(String text) {
304 String oldValue = this.text;
305 this.text = text;
306 firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);
307 updateDisplayedMnemonicIndex(text, getMnemonic());
308
309 if (accessibleContext != null) {
310 accessibleContext.firePropertyChange(
311 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
312 oldValue, text);
313 }
314 if (text == null || oldValue == null || !text.equals(oldValue)) {
315 revalidate();
316 repaint();
317 }
318 }
319
320
321 /**
322 * Returns the state of the button. True if the
323 * toggle button is selected, false if it's not.
324 * @return true if the toggle button is selected, otherwise false
325 */
326 public boolean isSelected() {
327 return model.isSelected();
328 }
329
330 /**
331 * Sets the state of the button. Note that this method does not
332 * trigger an <code>actionEvent</code>.
333 * Call <code>doClick</code> to perform a programatic action change.
334 *
335 * @param b true if the button is selected, otherwise false
336 */
337 public void setSelected(boolean b) {
338 boolean oldValue = isSelected();
339
340 // TIGER - 4840653
341 // Removed code which fired an AccessibleState.SELECTED
342 // PropertyChangeEvent since this resulted in two
343 // identical events being fired since
344 // AbstractButton.fireItemStateChanged also fires the
345 // same event. This caused screen readers to speak the
346 // name of the item twice.
347
348 model.setSelected(b);
349 }
350
351 /**
352 * Programmatically perform a "click". This does the same
353 * thing as if the user had pressed and released the button.
354 */
355 public void doClick() {
356 doClick(68);
357 }
358
359 /**
360 * Programmatically perform a "click". This does the same
361 * thing as if the user had pressed and released the button.
362 * The button stays visually "pressed" for <code>pressTime</code>
363 * milliseconds.
364 *
365 * @param pressTime the time to "hold down" the button, in milliseconds
366 */
367 public void doClick(int pressTime) {
368 Dimension size = getSize();
369 model.setArmed(true);
370 model.setPressed(true);
371 paintImmediately(new Rectangle(0,0, size.width, size.height));
372 try {
373 Thread.currentThread().sleep(pressTime);
374 } catch(InterruptedException ie) {
375 }
376 model.setPressed(false);
377 model.setArmed(false);
378 }
379
380 /**
381 * Sets space for margin between the button's border and
382 * the label. Setting to <code>null</code> will cause the button to
383 * use the default margin. The button's default <code>Border</code>
384 * object will use this value to create the proper margin.
385 * However, if a non-default border is set on the button,
386 * it is that <code>Border</code> object's responsibility to create the
387 * appropriate margin space (else this property will
388 * effectively be ignored).
389 *
390 * @param m the space between the border and the label
391 *
392 * @beaninfo
393 * bound: true
394 * attribute: visualUpdate true
395 * description: The space between the button's border and the label.
396 */
397 public void setMargin(Insets m) {
398 // Cache the old margin if it comes from the UI
399 if(m instanceof UIResource) {
400 defaultMargin = m;
401 } else if(margin instanceof UIResource) {
402 defaultMargin = margin;
403 }
404
405 // If the client passes in a null insets, restore the margin
406 // from the UI if possible
407 if(m == null && defaultMargin != null) {
408 m = defaultMargin;
409 }
410
411 Insets old = margin;
412 margin = m;
413 firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
414 if (old == null || !old.equals(m)) {
415 revalidate();
416 repaint();
417 }
418 }
419
420 /**
421 * Returns the margin between the button's border and
422 * the label.
423 *
424 * @return an <code>Insets</code> object specifying the margin
425 * between the botton's border and the label
426 * @see #setMargin
427 */
428 public Insets getMargin() {
429 return (margin == null) ? null : (Insets) margin.clone();
430 }
431
432 /**
433 * Returns the default icon.
434 * @return the default <code>Icon</code>
435 * @see #setIcon
436 */
437 public Icon getIcon() {
438 return defaultIcon;
439 }
440
441 /**
442 * Sets the button's default icon. This icon is
443 * also used as the "pressed" and "disabled" icon if
444 * there is no explicitly set pressed icon.
445 *
446 * @param defaultIcon the icon used as the default image
447 * @see #getIcon
448 * @see #setPressedIcon
449 * @beaninfo
450 * bound: true
451 * attribute: visualUpdate true
452 * description: The button's default icon
453 */
454 public void setIcon(Icon defaultIcon) {
455 Icon oldValue = this.defaultIcon;
456 this.defaultIcon = defaultIcon;
457
458 /* If the default icon has really changed and we had
459 * generated the disabled icon for this component,
460 * (i.e. setDisabledIcon() was never called) then
461 * clear the disabledIcon field.
462 */
463 if (defaultIcon != oldValue && (disabledIcon instanceof UIResource)) {
464 disabledIcon = null;
465 }
466
467 firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
468 if (accessibleContext != null) {
469 accessibleContext.firePropertyChange(
470 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
471 oldValue, defaultIcon);
472 }
473 if (defaultIcon != oldValue) {
474 if (defaultIcon == null || oldValue == null ||
475 defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
476 defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
477 revalidate();
478 }
479 repaint();
480 }
481 }
482
483 /**
484 * Returns the pressed icon for the button.
485 * @return the <code>pressedIcon</code> property
486 * @see #setPressedIcon
487 */
488 public Icon getPressedIcon() {
489 return pressedIcon;
490 }
491
492 /**
493 * Sets the pressed icon for the button.
494 * @param pressedIcon the icon used as the "pressed" image
495 * @see #getPressedIcon
496 * @beaninfo
497 * bound: true
498 * attribute: visualUpdate true
499 * description: The pressed icon for the button.
500 */
501 public void setPressedIcon(Icon pressedIcon) {
502 Icon oldValue = this.pressedIcon;
503 this.pressedIcon = pressedIcon;
504 firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
505 if (accessibleContext != null) {
506 accessibleContext.firePropertyChange(
507 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
508 oldValue, pressedIcon);
509 }
510 if (pressedIcon != oldValue) {
511 if (getModel().isPressed()) {
512 repaint();
513 }
514 }
515 }
516
517 /**
518 * Returns the selected icon for the button.
519 * @return the <code>selectedIcon</code> property
520 * @see #setSelectedIcon
521 */
522 public Icon getSelectedIcon() {
523 return selectedIcon;
524 }
525
526 /**
527 * Sets the selected icon for the button.
528 * @param selectedIcon the icon used as the "selected" image
529 * @see #getSelectedIcon
530 * @beaninfo
531 * bound: true
532 * attribute: visualUpdate true
533 * description: The selected icon for the button.
534 */
535 public void setSelectedIcon(Icon selectedIcon) {
536 Icon oldValue = this.selectedIcon;
537 this.selectedIcon = selectedIcon;
538
539 /* If the default selected icon has really changed and we had
540 * generated the disabled selected icon for this component,
541 * (i.e. setDisabledSelectedIcon() was never called) then
542 * clear the disabledSelectedIcon field.
543 */
544 if (selectedIcon != oldValue &&
545 disabledSelectedIcon instanceof UIResource) {
546
547 disabledSelectedIcon = null;
548 }
549
550 firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
551 if (accessibleContext != null) {
552 accessibleContext.firePropertyChange(
553 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
554 oldValue, selectedIcon);
555 }
556 if (selectedIcon != oldValue) {
557 if (isSelected()) {
558 repaint();
559 }
560 }
561 }
562
563 /**
564 * Returns the rollover icon for the button.
565 * @return the <code>rolloverIcon</code> property
566 * @see #setRolloverIcon
567 */
568 public Icon getRolloverIcon() {
569 return rolloverIcon;
570 }
571
572 /**
573 * Sets the rollover icon for the button.
574 * @param rolloverIcon the icon used as the "rollover" image
575 * @see #getRolloverIcon
576 * @beaninfo
577 * bound: true
578 * attribute: visualUpdate true
579 * description: The rollover icon for the button.
580 */
581 public void setRolloverIcon(Icon rolloverIcon) {
582 Icon oldValue = this.rolloverIcon;
583 this.rolloverIcon = rolloverIcon;
584 firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
585 if (accessibleContext != null) {
586 accessibleContext.firePropertyChange(
587 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
588 oldValue, rolloverIcon);
589 }
590 setRolloverEnabled(true);
591 if (rolloverIcon != oldValue) {
592 // No way to determine whether we are currently in
593 // a rollover state, so repaint regardless
594 repaint();
595 }
596
597 }
598
599 /**
600 * Returns the rollover selection icon for the button.
601 * @return the <code>rolloverSelectedIcon</code> property
602 * @see #setRolloverSelectedIcon
603 */
604 public Icon getRolloverSelectedIcon() {
605 return rolloverSelectedIcon;
606 }
607
608 /**
609 * Sets the rollover selected icon for the button.
610 * @param rolloverSelectedIcon the icon used as the
611 * "selected rollover" image
612 * @see #getRolloverSelectedIcon
613 * @beaninfo
614 * bound: true
615 * attribute: visualUpdate true
616 * description: The rollover selected icon for the button.
617 */
618 public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
619 Icon oldValue = this.rolloverSelectedIcon;
620 this.rolloverSelectedIcon = rolloverSelectedIcon;
621 firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
622 if (accessibleContext != null) {
623 accessibleContext.firePropertyChange(
624 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
625 oldValue, rolloverSelectedIcon);
626 }
627 setRolloverEnabled(true);
628 if (rolloverSelectedIcon != oldValue) {
629 // No way to determine whether we are currently in
630 // a rollover state, so repaint regardless
631 if (isSelected()) {
632 repaint();
633 }
634 }
635 }
636
637 /**
638 * Returns the icon used by the button when it's disabled.
639 * If no disabled icon has been set this will forward the call to
640 * the look and feel to construct an appropriate disabled Icon.
641 * <p>
642 * Some look and feels might not render the disabled Icon, in which
643 * case they will ignore this.
644 *
645 * @return the <code>disabledIcon</code> property
646 * @see #getPressedIcon
647 * @see #setDisabledIcon
648 * @see javax.swing.LookAndFeel#getDisabledIcon
649 */
650 @Transient
651 public Icon getDisabledIcon() {
652 if (disabledIcon == null) {
653 disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
654 if (disabledIcon != null) {
655 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);
656 }
657 }
658 return disabledIcon;
659 }
660
661 /**
662 * Sets the disabled icon for the button.
663 * @param disabledIcon the icon used as the disabled image
664 * @see #getDisabledIcon
665 * @beaninfo
666 * bound: true
667 * attribute: visualUpdate true
668 * description: The disabled icon for the button.
669 */
670 public void setDisabledIcon(Icon disabledIcon) {
671 Icon oldValue = this.disabledIcon;
672 this.disabledIcon = disabledIcon;
673 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
674 if (accessibleContext != null) {
675 accessibleContext.firePropertyChange(
676 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
677 oldValue, disabledIcon);
678 }
679 if (disabledIcon != oldValue) {
680 if (!isEnabled()) {
681 repaint();
682 }
683 }
684 }
685
686 /**
687 * Returns the icon used by the button when it's disabled and selected.
688 * If no disabled selection icon has been set, this will forward
689 * the call to the LookAndFeel to construct an appropriate disabled
690 * Icon from the selection icon if it has been set and to
691 * <code>getDisabledIcon()</code> otherwise.
692 * <p>
693 * Some look and feels might not render the disabled selected Icon, in
694 * which case they will ignore this.
695 *
696 * @return the <code>disabledSelectedIcon</code> property
697 * @see #getDisabledIcon
698 * @see #setDisabledSelectedIcon
699 * @see javax.swing.LookAndFeel#getDisabledSelectedIcon
700 */
701 public Icon getDisabledSelectedIcon() {
702 if (disabledSelectedIcon == null) {
703 if (selectedIcon != null) {
704 disabledSelectedIcon = UIManager.getLookAndFeel().
705 getDisabledSelectedIcon(this, getSelectedIcon());
706 } else {
707 return getDisabledIcon();
708 }
709 }
710 return disabledSelectedIcon;
711 }
712
713 /**
714 * Sets the disabled selection icon for the button.
715 * @param disabledSelectedIcon the icon used as the disabled
716 * selection image
717 * @see #getDisabledSelectedIcon
718 * @beaninfo
719 * bound: true
720 * attribute: visualUpdate true
721 * description: The disabled selection icon for the button.
722 */
723 public void setDisabledSelectedIcon(Icon disabledSelectedIcon) {
724 Icon oldValue = this.disabledSelectedIcon;
725 this.disabledSelectedIcon = disabledSelectedIcon;
726 firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, oldValue, disabledSelectedIcon);
727 if (accessibleContext != null) {
728 accessibleContext.firePropertyChange(
729 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
730 oldValue, disabledSelectedIcon);
731 }
732 if (disabledSelectedIcon != oldValue) {
733 if (disabledSelectedIcon == null || oldValue == null ||
734 disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
735 disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
736 revalidate();
737 }
738 if (!isEnabled() && isSelected()) {
739 repaint();
740 }
741 }
742 }
743
744 /**
745 * Returns the vertical alignment of the text and icon.
746 *
747 * @return the <code>verticalAlignment</code> property, one of the
748 * following values:
749 * <ul>
750 * <li>{@code SwingConstants.CENTER} (the default)
751 * <li>{@code SwingConstants.TOP}
752 * <li>{@code SwingConstants.BOTTOM}
753 * </ul>
754 */
755 public int getVerticalAlignment() {
756 return verticalAlignment;
757 }
758
759 /**
760 * Sets the vertical alignment of the icon and text.
761 * @param alignment one of the following values:
762 * <ul>
763 * <li>{@code SwingConstants.CENTER} (the default)
764 * <li>{@code SwingConstants.TOP}
765 * <li>{@code SwingConstants.BOTTOM}
766 * </ul>
767 * @throws IllegalArgumentException if the alignment is not one of the legal
768 * values listed above
769 * @beaninfo
770 * bound: true
771 * enum: TOP SwingConstants.TOP
772 * CENTER SwingConstants.CENTER
773 * BOTTOM SwingConstants.BOTTOM
774 * attribute: visualUpdate true
775 * description: The vertical alignment of the icon and text.
776 */
777 public void setVerticalAlignment(int alignment) {
778 if (alignment == verticalAlignment) return;
779 int oldValue = verticalAlignment;
780 verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
781 firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment); repaint();
782 }
783
784 /**
785 * Returns the horizontal alignment of the icon and text.
786 * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
787 * but subclasses such as {@code JCheckBox} may use a different default.
788 *
789 * @return the <code>horizontalAlignment</code> property,
790 * one of the following values:
791 * <ul>
792 * <li>{@code SwingConstants.RIGHT}
793 * <li>{@code SwingConstants.LEFT}
794 * <li>{@code SwingConstants.CENTER}
795 * <li>{@code SwingConstants.LEADING}
796 * <li>{@code SwingConstants.TRAILING}
797 * </ul>
798 */
799 public int getHorizontalAlignment() {
800 return horizontalAlignment;
801 }
802
803 /**
804 * Sets the horizontal alignment of the icon and text.
805 * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
806 * but subclasses such as {@code JCheckBox} may use a different default.
807 *
808 * @param alignment the alignment value, one of the following values:
809 * <ul>
810 * <li>{@code SwingConstants.RIGHT}
811 * <li>{@code SwingConstants.LEFT}
812 * <li>{@code SwingConstants.CENTER}
813 * <li>{@code SwingConstants.LEADING}
814 * <li>{@code SwingConstants.TRAILING}
815 * </ul>
816 * @throws IllegalArgumentException if the alignment is not one of the
817 * valid values
818 * @beaninfo
819 * bound: true
820 * enum: LEFT SwingConstants.LEFT
821 * CENTER SwingConstants.CENTER
822 * RIGHT SwingConstants.RIGHT
823 * LEADING SwingConstants.LEADING
824 * TRAILING SwingConstants.TRAILING
825 * attribute: visualUpdate true
826 * description: The horizontal alignment of the icon and text.
827 */
828 public void setHorizontalAlignment(int alignment) {
829 if (alignment == horizontalAlignment) return;
830 int oldValue = horizontalAlignment;
831 horizontalAlignment = checkHorizontalKey(alignment,
832 "horizontalAlignment");
833 firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
834 oldValue, horizontalAlignment);
835 repaint();
836 }
837
838
839 /**
840 * Returns the vertical position of the text relative to the icon.
841 * @return the <code>verticalTextPosition</code> property,
842 * one of the following values:
843 * <ul>
844 * <li>{@code SwingConstants.CENTER} (the default)
845 * <li>{@code SwingConstants.TOP}
846 * <li>{@code SwingConstants.BOTTOM}
847 * </ul>
848 */
849 public int getVerticalTextPosition() {
850 return verticalTextPosition;
851 }
852
853 /**
854 * Sets the vertical position of the text relative to the icon.
855 * @param textPosition one of the following values:
856 * <ul>
857 * <li>{@code SwingConstants.CENTER} (the default)
858 * <li>{@code SwingConstants.TOP}
859 * <li>{@code SwingConstants.BOTTOM}
860 * </ul>
861 * @beaninfo
862 * bound: true
863 * enum: TOP SwingConstants.TOP
864 * CENTER SwingConstants.CENTER
865 * BOTTOM SwingConstants.BOTTOM
866 * attribute: visualUpdate true
867 * description: The vertical position of the text relative to the icon.
868 */
869 public void setVerticalTextPosition(int textPosition) {
870 if (textPosition == verticalTextPosition) return;
871 int oldValue = verticalTextPosition;
872 verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
873 firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
874 revalidate();
875 repaint();
876 }
877
878 /**
879 * Returns the horizontal position of the text relative to the icon.
880 * @return the <code>horizontalTextPosition</code> property,
881 * one of the following values:
882 * <ul>
883 * <li>{@code SwingConstants.RIGHT}
884 * <li>{@code SwingConstants.LEFT}
885 * <li>{@code SwingConstants.CENTER}
886 * <li>{@code SwingConstants.LEADING}
887 * <li>{@code SwingConstants.TRAILING} (the default)
888 * </ul>
889 */
890 public int getHorizontalTextPosition() {
891 return horizontalTextPosition;
892 }
893
894 /**
895 * Sets the horizontal position of the text relative to the icon.
896 * @param textPosition one of the following values:
897 * <ul>
898 * <li>{@code SwingConstants.RIGHT}
899 * <li>{@code SwingConstants.LEFT}
900 * <li>{@code SwingConstants.CENTER}
901 * <li>{@code SwingConstants.LEADING}
902 * <li>{@code SwingConstants.TRAILING} (the default)
903 * </ul>
904 * @exception IllegalArgumentException if <code>textPosition</code>
905 * is not one of the legal values listed above
906 * @beaninfo
907 * bound: true
908 * enum: LEFT SwingConstants.LEFT
909 * CENTER SwingConstants.CENTER
910 * RIGHT SwingConstants.RIGHT
911 * LEADING SwingConstants.LEADING
912 * TRAILING SwingConstants.TRAILING
913 * attribute: visualUpdate true
914 * description: The horizontal position of the text relative to the icon.
915 */
916 public void setHorizontalTextPosition(int textPosition) {
917 if (textPosition == horizontalTextPosition) return;
918 int oldValue = horizontalTextPosition;
919 horizontalTextPosition = checkHorizontalKey(textPosition,
920 "horizontalTextPosition");
921 firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
922 oldValue,
923 horizontalTextPosition);
924 revalidate();
925 repaint();
926 }
927
928 /**
929 * Returns the amount of space between the text and the icon
930 * displayed in this button.
931 *
932 * @return an int equal to the number of pixels between the text
933 * and the icon.
934 * @since 1.4
935 * @see #setIconTextGap
936 */
937 public int getIconTextGap() {
938 return iconTextGap;
939 }
940
941 /**
942 * If both the icon and text properties are set, this property
943 * defines the space between them.
944 * <p>
945 * The default value of this property is 4 pixels.
946 * <p>
947 * This is a JavaBeans bound property.
948 *
949 * @since 1.4
950 * @see #getIconTextGap
951 * @beaninfo
952 * bound: true
953 * attribute: visualUpdate true
954 * description: If both the icon and text properties are set, this
955 * property defines the space between them.
956 */
957 public void setIconTextGap(int iconTextGap) {
958 int oldValue = this.iconTextGap;
959 this.iconTextGap = iconTextGap;
960 iconTextGapSet = true;
961 firePropertyChange("iconTextGap", oldValue, iconTextGap);
962 if (iconTextGap != oldValue) {
963 revalidate();
964 repaint();
965 }
966 }
967
968 /**
969 * Verify that the {@code key} argument is a legal value for the
970 * {@code horizontalAlignment} and {@code horizontalTextPosition}
971 * properties. Valid values are:
972 * <ul>
973 * <li>{@code SwingConstants.RIGHT}
974 * <li>{@code SwingConstants.LEFT}
975 * <li>{@code SwingConstants.CENTER}
976 * <li>{@code SwingConstants.LEADING}
977 * <li>{@code SwingConstants.TRAILING}
978 * </ul>
979 *
980 * @param key the property value to check
981 * @param exception the message to use in the
982 * {@code IllegalArgumentException} that is thrown for an invalid
983 * value
984 * @exception IllegalArgumentException if key is not one of the legal
985 * values listed above
986 * @see #setHorizontalTextPosition
987 * @see #setHorizontalAlignment
988 */
989 protected int checkHorizontalKey(int key, String exception) {
990 if ((key == LEFT) ||
991 (key == CENTER) ||
992 (key == RIGHT) ||
993 (key == LEADING) ||
994 (key == TRAILING)) {
995 return key;
996 } else {
997 throw new IllegalArgumentException(exception);
998 }
999 }
1000
1001 /**
1002 * Verify that the {@code key} argument is a legal value for the
1003 * vertical properties. Valid values are:
1004 * <ul>
1005 * <li>{@code SwingConstants.CENTER}
1006 * <li>{@code SwingConstants.TOP}
1007 * <li>{@code SwingConstants.BOTTOM}
1008 * </ul>
1009 *
1010 * @param key the property value to check
1011 * @param exception the message to use in the
1012 * {@code IllegalArgumentException} that is thrown for an invalid
1013 * value
1014 * @exception IllegalArgumentException if key is not one of the legal
1015 * values listed above
1016 */
1017 protected int checkVerticalKey(int key, String exception) {
1018 if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {
1019 return key;
1020 } else {
1021 throw new IllegalArgumentException(exception);
1022 }
1023 }
1024
1025 /**
1026 *{@inheritDoc}
1027 *
1028 * @since 1.6
1029 */
1030 public void removeNotify() {
1031 super.removeNotify();
1032 if(isRolloverEnabled()) {
1033 getModel().setRollover(false);
1034 }
1035 }
1036
1037 /**
1038 * Sets the action command for this button.
1039 * @param actionCommand the action command for this button
1040 */
1041 public void setActionCommand(String actionCommand) {
1042 getModel().setActionCommand(actionCommand);
1043 }
1044
1045 /**
1046 * Returns the action command for this button.
1047 * @return the action command for this button
1048 */
1049 public String getActionCommand() {
1050 String ac = getModel().getActionCommand();
1051 if(ac == null) {
1052 ac = getText();
1053 }
1054 return ac;
1055 }
1056
1057 private Action action;
1058 private PropertyChangeListener actionPropertyChangeListener;
1059
1060 /**
1061 * Sets the <code>Action</code>.
1062 * The new <code>Action</code> replaces any previously set
1063 * <code>Action</code> but does not affect <code>ActionListeners</code>
1064 * independently added with <code>addActionListener</code>.
1065 * If the <code>Action</code> is already a registered
1066 * <code>ActionListener</code> for the button, it is not re-registered.
1067 * <p>
1068 * Setting the <code>Action</code> results in immediately changing
1069 * all the properties described in <a href="Action.html#buttonActions">
1070 * Swing Components Supporting <code>Action</code></a>.
1071 * Subsequently, the button's properties are automatically updated
1072 * as the <code>Action</code>'s properties change.
1073 * <p>
1074 * This method uses three other methods to set
1075 * and help track the <code>Action</code>'s property values.
1076 * It uses the <code>configurePropertiesFromAction</code> method
1077 * to immediately change the button's properties.
1078 * To track changes in the <code>Action</code>'s property values,
1079 * this method registers the <code>PropertyChangeListener</code>
1080 * returned by <code>createActionPropertyChangeListener</code>. The
1081 * default {@code PropertyChangeListener} invokes the
1082 * {@code actionPropertyChanged} method when a property in the
1083 * {@code Action} changes.
1084 *
1085 * @param a the <code>Action</code> for the <code>AbstractButton</code>,
1086 * or <code>null</code>
1087 * @since 1.3
1088 * @see Action
1089 * @see #getAction
1090 * @see #configurePropertiesFromAction
1091 * @see #createActionPropertyChangeListener
1092 * @see #actionPropertyChanged
1093 * @beaninfo
1094 * bound: true
1095 * attribute: visualUpdate true
1096 * description: the Action instance connected with this ActionEvent source
1097 */
1098 public void setAction(Action a) {
1099 Action oldValue = getAction();
1100 if (action==null || !action.equals(a)) {
1101 action = a;
1102 if (oldValue!=null) {
1103 removeActionListener(oldValue);
1104 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1105 actionPropertyChangeListener = null;
1106 }
1107 configurePropertiesFromAction(action);
1108 if (action!=null) {
1109 // Don't add if it is already a listener
1110 if (!isListener(ActionListener.class, action)) {
1111 addActionListener(action);
1112 }
1113 // Reverse linkage:
1114 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1115 action.addPropertyChangeListener(actionPropertyChangeListener);
1116 }
1117 firePropertyChange("action", oldValue, action);
1118 }
1119 }
1120
1121 private boolean isListener(Class c, ActionListener a) {
1122 boolean isListener = false;
1123 Object[] listeners = listenerList.getListenerList();
1124 for (int i = listeners.length-2; i>=0; i-=2) {
1125 if (listeners[i]==c && listeners[i+1]==a) {
1126 isListener=true;
1127 }
1128 }
1129 return isListener;
1130 }
1131
1132 /**
1133 * Returns the currently set <code>Action</code> for this
1134 * <code>ActionEvent</code> source, or <code>null</code>
1135 * if no <code>Action</code> is set.
1136 *
1137 * @return the <code>Action</code> for this <code>ActionEvent</code>
1138 * source, or <code>null</code>
1139 * @since 1.3
1140 * @see Action
1141 * @see #setAction
1142 */
1143 public Action getAction() {
1144 return action;
1145 }
1146
1147 /**
1148 * Sets the properties on this button to match those in the specified
1149 * <code>Action</code>. Refer to <a href="Action.html#buttonActions">
1150 * Swing Components Supporting <code>Action</code></a> for more
1151 * details as to which properties this sets.
1152 *
1153 * @param a the <code>Action</code> from which to get the properties,
1154 * or <code>null</code>
1155 * @since 1.3
1156 * @see Action
1157 * @see #setAction
1158 */
1159 protected void configurePropertiesFromAction(Action a) {
1160 setMnemonicFromAction(a);
1161 setTextFromAction(a, false);
1162 AbstractAction.setToolTipTextFromAction(this, a);
1163 setIconFromAction(a);
1164 setActionCommandFromAction(a);
1165 AbstractAction.setEnabledFromAction(this, a);
1166 if (AbstractAction.hasSelectedKey(a) &&
1167 shouldUpdateSelectedStateFromAction()) {
1168 setSelectedFromAction(a);
1169 }
1170 setDisplayedMnemonicIndexFromAction(a, false);
1171 }
1172
1173 void clientPropertyChanged(Object key, Object oldValue,
1174 Object newValue) {
1175 if (key == "hideActionText") {
1176 boolean current = (newValue instanceof Boolean) ?
1177 (Boolean)newValue : false;
1178 if (getHideActionText() != current) {
1179 setHideActionText(current);
1180 }
1181 }
1182 }
1183
1184 /**
1185 * Button subclasses that support mirroring the selected state from
1186 * the action should override this to return true. AbstractButton's
1187 * implementation returns false.
1188 */
1189 boolean shouldUpdateSelectedStateFromAction() {
1190 return false;
1191 }
1192
1193 /**
1194 * Updates the button's state in response to property changes in the
1195 * associated action. This method is invoked from the
1196 * {@code PropertyChangeListener} returned from
1197 * {@code createActionPropertyChangeListener}. Subclasses do not normally
1198 * need to invoke this. Subclasses that support additional {@code Action}
1199 * properties should override this and
1200 * {@code configurePropertiesFromAction}.
1201 * <p>
1202 * Refer to the table at <a href="Action.html#buttonActions">
1203 * Swing Components Supporting <code>Action</code></a> for a list of
1204 * the properties this method sets.
1205 *
1206 * @param action the <code>Action</code> associated with this button
1207 * @param propertyName the name of the property that changed
1208 * @since 1.6
1209 * @see Action
1210 * @see #configurePropertiesFromAction
1211 */
1212 protected void actionPropertyChanged(Action action, String propertyName) {
1213 if (propertyName == Action.NAME) {
1214 setTextFromAction(action, true);
1215 } else if (propertyName == "enabled") {
1216 AbstractAction.setEnabledFromAction(this, action);
1217 } else if (propertyName == Action.SHORT_DESCRIPTION) {
1218 AbstractAction.setToolTipTextFromAction(this, action);
1219 } else if (propertyName == Action.SMALL_ICON) {
1220 smallIconChanged(action);
1221 } else if (propertyName == Action.MNEMONIC_KEY) {
1222 setMnemonicFromAction(action);
1223 } else if (propertyName == Action.ACTION_COMMAND_KEY) {
1224 setActionCommandFromAction(action);
1225 } else if (propertyName == Action.SELECTED_KEY &&
1226 AbstractAction.hasSelectedKey(action) &&
1227 shouldUpdateSelectedStateFromAction()) {
1228 setSelectedFromAction(action);
1229 } else if (propertyName == Action.DISPLAYED_MNEMONIC_INDEX_KEY) {
1230 setDisplayedMnemonicIndexFromAction(action, true);
1231 } else if (propertyName == Action.LARGE_ICON_KEY) {
1232 largeIconChanged(action);
1233 }
1234 }
1235
1236 private void setDisplayedMnemonicIndexFromAction(
1237 Action a, boolean fromPropertyChange) {
1238 Integer iValue = (a == null) ? null :
1239 (Integer)a.getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY);
1240 if (fromPropertyChange || iValue != null) {
1241 int value;
1242 if (iValue == null) {
1243 value = -1;
1244 } else {
1245 value = iValue;
1246 String text = getText();
1247 if (text == null || value >= text.length()) {
1248 value = -1;
1249 }
1250 }
1251 setDisplayedMnemonicIndex(value);
1252 }
1253 }
1254
1255 private void setMnemonicFromAction(Action a) {
1256 Integer n = (a == null) ? null :
1257 (Integer)a.getValue(Action.MNEMONIC_KEY);
1258 setMnemonic((n == null) ? '\0' : n);
1259 }
1260
1261 private void setTextFromAction(Action a, boolean propertyChange) {
1262 boolean hideText = getHideActionText();
1263 if (!propertyChange) {
1264 setText((a != null && !hideText) ?
1265 (String)a.getValue(Action.NAME) : null);
1266 }
1267 else if (!hideText) {
1268 setText((String)a.getValue(Action.NAME));
1269 }
1270 }
1271
1272 void setIconFromAction(Action a) {
1273 Icon icon = null;
1274 if (a != null) {
1275 icon = (Icon)a.getValue(Action.LARGE_ICON_KEY);
1276 if (icon == null) {
1277 icon = (Icon)a.getValue(Action.SMALL_ICON);
1278 }
1279 }
1280 setIcon(icon);
1281 }
1282
1283 void smallIconChanged(Action a) {
1284 if (a.getValue(Action.LARGE_ICON_KEY) == null) {
1285 setIconFromAction(a);
1286 }
1287 }
1288
1289 void largeIconChanged(Action a) {
1290 setIconFromAction(a);
1291 }
1292
1293 private void setActionCommandFromAction(Action a) {
1294 setActionCommand((a != null) ?
1295 (String)a.getValue(Action.ACTION_COMMAND_KEY) :
1296 null);
1297 }
1298
1299 /**
1300 * Sets the seleted state of the button from the action. This is defined
1301 * here, but not wired up. Subclasses like JToggleButton and
1302 * JCheckBoxMenuItem make use of it.
1303 *
1304 * @param a the Action
1305 */
1306 private void setSelectedFromAction(Action a) {
1307 boolean selected = false;
1308 if (a != null) {
1309 selected = AbstractAction.isSelected(a);
1310 }
1311 if (selected != isSelected()) {
1312 // This won't notify ActionListeners, but that should be
1313 // ok as the change is coming from the Action.
1314 setSelected(selected);
1315 // Make sure the change actually took effect
1316 if (!selected && isSelected()) {
1317 if (getModel() instanceof DefaultButtonModel) {
1318 ButtonGroup group = (ButtonGroup)
1319 ((DefaultButtonModel)getModel()).getGroup();
1320 if (group != null) {
1321 group.clearSelection();
1322 }
1323 }
1324 }
1325 }
1326 }
1327
1328 /**
1329 * Creates and returns a <code>PropertyChangeListener</code> that is
1330 * responsible for listening for changes from the specified
1331 * <code>Action</code> and updating the appropriate properties.
1332 * <p>
1333 * <b>Warning:</b> If you subclass this do not create an anonymous
1334 * inner class. If you do the lifetime of the button will be tied to
1335 * that of the <code>Action</code>.
1336 *
1337 * @param a the button's action
1338 * @since 1.3
1339 * @see <a href="#actions">Actions</a>
1340 * @see Action
1341 * @see #setAction
1342 */
1343 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1344 return createActionPropertyChangeListener0(a);
1345 }
1346
1347
1348 PropertyChangeListener createActionPropertyChangeListener0(Action a) {
1349 return new ButtonActionPropertyChangeListener(this, a);
1350 }
1351
1352 private static class ButtonActionPropertyChangeListener
1353 extends ActionPropertyChangeListener<AbstractButton> {
1354 ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
1355 super(b, a);
1356 }
1357 protected void actionPropertyChanged(AbstractButton button,
1358 Action action,
1359 PropertyChangeEvent e) {
1360 if (AbstractAction.shouldReconfigure(e)) {
1361 button.configurePropertiesFromAction(action);
1362 } else {
1363 button.actionPropertyChanged(action, e.getPropertyName());
1364 }
1365 }
1366 }
1367
1368 /**
1369 * Gets the <code>borderPainted</code> property.
1370 *
1371 * @return the value of the <code>borderPainted</code> property
1372 * @see #setBorderPainted
1373 */
1374 public boolean isBorderPainted() {
1375 return paintBorder;
1376 }
1377
1378 /**
1379 * Sets the <code>borderPainted</code> property.
1380 * If <code>true</code> and the button has a border,
1381 * the border is painted. The default value for the
1382 * <code>borderPainted</code> property is <code>true</code>.
1383 *
1384 * @param b if true and border property is not <code>null</code>,
1385 * the border is painted
1386 * @see #isBorderPainted
1387 * @beaninfo
1388 * bound: true
1389 * attribute: visualUpdate true
1390 * description: Whether the border should be painted.
1391 */
1392 public void setBorderPainted(boolean b) {
1393 boolean oldValue = paintBorder;
1394 paintBorder = b;
1395 borderPaintedSet = true;
1396 firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
1397 if (b != oldValue) {
1398 revalidate();
1399 repaint();
1400 }
1401 }
1402
1403 /**
1404 * Paint the button's border if <code>BorderPainted</code>
1405 * property is true and the button has a border.
1406 * @param g the <code>Graphics</code> context in which to paint
1407 *
1408 * @see #paint
1409 * @see #setBorder
1410 */
1411 protected void paintBorder(Graphics g) {
1412 if (isBorderPainted()) {
1413 super.paintBorder(g);
1414 }
1415 }
1416
1417 /**
1418 * Gets the <code>paintFocus</code> property.
1419 *
1420 * @return the <code>paintFocus</code> property
1421 * @see #setFocusPainted
1422 */
1423 public boolean isFocusPainted() {
1424 return paintFocus;
1425 }
1426
1427 /**
1428 * Sets the <code>paintFocus</code> property, which must
1429 * be <code>true</code> for the focus state to be painted.
1430 * The default value for the <code>paintFocus</code> property
1431 * is <code>true</code>.
1432 * Some look and feels might not paint focus state;
1433 * they will ignore this property.
1434 *
1435 * @param b if <code>true</code>, the focus state should be painted
1436 * @see #isFocusPainted
1437 * @beaninfo
1438 * bound: true
1439 * attribute: visualUpdate true
1440 * description: Whether focus should be painted
1441 */
1442 public void setFocusPainted(boolean b) {
1443 boolean oldValue = paintFocus;
1444 paintFocus = b;
1445 firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
1446 if (b != oldValue && isFocusOwner()) {
1447 revalidate();
1448 repaint();
1449 }
1450 }
1451
1452 /**
1453 * Gets the <code>contentAreaFilled</code> property.
1454 *
1455 * @return the <code>contentAreaFilled</code> property
1456 * @see #setContentAreaFilled
1457 */
1458 public boolean isContentAreaFilled() {
1459 return contentAreaFilled;
1460 }
1461
1462 /**
1463 * Sets the <code>contentAreaFilled</code> property.
1464 * If <code>true</code> the button will paint the content
1465 * area. If you wish to have a transparent button, such as
1466 * an icon only button, for example, then you should set
1467 * this to <code>false</code>. Do not call <code>setOpaque(false)</code>.
1468 * The default value for the the <code>contentAreaFilled</code>
1469 * property is <code>true</code>.
1470 * <p>
1471 * This function may cause the component's opaque property to change.
1472 * <p>
1473 * The exact behavior of calling this function varies on a
1474 * component-by-component and L&F-by-L&F basis.
1475 *
1476 * @param b if true, the content should be filled; if false
1477 * the content area is not filled
1478 * @see #isContentAreaFilled
1479 * @see #setOpaque
1480 * @beaninfo
1481 * bound: true
1482 * attribute: visualUpdate true
1483 * description: Whether the button should paint the content area
1484 * or leave it transparent.
1485 */
1486 public void setContentAreaFilled(boolean b) {
1487 boolean oldValue = contentAreaFilled;
1488 contentAreaFilled = b;
1489 contentAreaFilledSet = true;
1490 firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
1491 if (b != oldValue) {
1492 repaint();
1493 }
1494 }
1495
1496 /**
1497 * Gets the <code>rolloverEnabled</code> property.
1498 *
1499 * @return the value of the <code>rolloverEnabled</code> property
1500 * @see #setRolloverEnabled
1501 */
1502 public boolean isRolloverEnabled() {
1503 return rolloverEnabled;
1504 }
1505
1506 /**
1507 * Sets the <code>rolloverEnabled</code> property, which
1508 * must be <code>true</code> for rollover effects to occur.
1509 * The default value for the <code>rolloverEnabled</code>
1510 * property is <code>false</code>.
1511 * Some look and feels might not implement rollover effects;
1512 * they will ignore this property.
1513 *
1514 * @param b if <code>true</code>, rollover effects should be painted
1515 * @see #isRolloverEnabled
1516 * @beaninfo
1517 * bound: true
1518 * attribute: visualUpdate true
1519 * description: Whether rollover effects should be enabled.
1520 */
1521 public void setRolloverEnabled(boolean b) {
1522 boolean oldValue = rolloverEnabled;
1523 rolloverEnabled = b;
1524 rolloverEnabledSet = true;
1525 firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
1526 if (b != oldValue) {
1527 repaint();
1528 }
1529 }
1530
1531 /**
1532 * Returns the keyboard mnemonic from the the current model.
1533 * @return the keyboard mnemonic from the model
1534 */
1535 public int getMnemonic() {
1536 return mnemonic;
1537 }
1538
1539 /**
1540 * Sets the keyboard mnemonic on the current model.
1541 * The mnemonic is the key which when combined with the look and feel's
1542 * mouseless modifier (usually Alt) will activate this button
1543 * if focus is contained somewhere within this button's ancestor
1544 * window.
1545 * <p>
1546 * A mnemonic must correspond to a single key on the keyboard
1547 * and should be specified using one of the <code>VK_XXX</code>
1548 * keycodes defined in <code>java.awt.event.KeyEvent</code>.
1549 * Mnemonics are case-insensitive, therefore a key event
1550 * with the corresponding keycode would cause the button to be
1551 * activated whether or not the Shift modifier was pressed.
1552 * <p>
1553 * If the character defined by the mnemonic is found within
1554 * the button's label string, the first occurrence of it
1555 * will be underlined to indicate the mnemonic to the user.
1556 *
1557 * @param mnemonic the key code which represents the mnemonic
1558 * @see java.awt.event.KeyEvent
1559 * @see #setDisplayedMnemonicIndex
1560 *
1561 * @beaninfo
1562 * bound: true
1563 * attribute: visualUpdate true
1564 * description: the keyboard character mnemonic
1565 */
1566 public void setMnemonic(int mnemonic) {
1567 int oldValue = getMnemonic();
1568 model.setMnemonic(mnemonic);
1569 updateMnemonicProperties();
1570 }
1571
1572 /**
1573 * This method is now obsolete, please use <code>setMnemonic(int)</code>
1574 * to set the mnemonic for a button. This method is only designed
1575 * to handle character values which fall between 'a' and 'z' or
1576 * 'A' and 'Z'.
1577 *
1578 * @param mnemonic a char specifying the mnemonic value
1579 * @see #setMnemonic(int)
1580 * @beaninfo
1581 * bound: true
1582 * attribute: visualUpdate true
1583 * description: the keyboard character mnemonic
1584 */
1585 public void setMnemonic(char mnemonic) {
1586 int vk = (int) mnemonic;
1587 if(vk >= 'a' && vk <='z')
1588 vk -= ('a' - 'A');
1589 setMnemonic(vk);
1590 }
1591
1592 /**
1593 * Provides a hint to the look and feel as to which character in the
1594 * text should be decorated to represent the mnemonic. Not all look and
1595 * feels may support this. A value of -1 indicates either there is no
1596 * mnemonic, the mnemonic character is not contained in the string, or
1597 * the developer does not wish the mnemonic to be displayed.
1598 * <p>
1599 * The value of this is updated as the properties relating to the
1600 * mnemonic change (such as the mnemonic itself, the text...).
1601 * You should only ever have to call this if
1602 * you do not wish the default character to be underlined. For example, if
1603 * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
1604 * to be decorated, as 'Save <u>A</u>s', you would have to invoke
1605 * <code>setDisplayedMnemonicIndex(5)</code> after invoking
1606 * <code>setMnemonic(KeyEvent.VK_A)</code>.
1607 *
1608 * @since 1.4
1609 * @param index Index into the String to underline
1610 * @exception IllegalArgumentException will be thrown if <code>index</code>
1611 * is >= length of the text, or < -1
1612 * @see #getDisplayedMnemonicIndex
1613 *
1614 * @beaninfo
1615 * bound: true
1616 * attribute: visualUpdate true
1617 * description: the index into the String to draw the keyboard character
1618 * mnemonic at
1619 */
1620 public void setDisplayedMnemonicIndex(int index)
1621 throws IllegalArgumentException {
1622 int oldValue = mnemonicIndex;
1623 if (index == -1) {
1624 mnemonicIndex = -1;
1625 } else {
1626 String text = getText();
1627 int textLength = (text == null) ? 0 : text.length();
1628 if (index < -1 || index >= textLength) { // index out of range
1629 throw new IllegalArgumentException("index == " + index);
1630 }
1631 }
1632 mnemonicIndex = index;
1633 firePropertyChange("displayedMnemonicIndex", oldValue, index);
1634 if (index != oldValue) {
1635 revalidate();
1636 repaint();
1637 }
1638 }
1639
1640 /**
1641 * Returns the character, as an index, that the look and feel should
1642 * provide decoration for as representing the mnemonic character.
1643 *
1644 * @since 1.4
1645 * @return index representing mnemonic character
1646 * @see #setDisplayedMnemonicIndex
1647 */
1648 public int getDisplayedMnemonicIndex() {
1649 return mnemonicIndex;
1650 }
1651
1652 /**
1653 * Update the displayedMnemonicIndex property. This method
1654 * is called when either text or mnemonic changes. The new
1655 * value of the displayedMnemonicIndex property is the index
1656 * of the first occurrence of mnemonic in text.
1657 */
1658 private void updateDisplayedMnemonicIndex(String text, int mnemonic) {
1659 setDisplayedMnemonicIndex(
1660 SwingUtilities.findDisplayedMnemonicIndex(text, mnemonic));
1661 }
1662
1663 /**
1664 * Brings the mnemonic property in accordance with model's mnemonic.
1665 * This is called when model's mnemonic changes. Also updates the
1666 * displayedMnemonicIndex property.
1667 */
1668 private void updateMnemonicProperties() {
1669 int newMnemonic = model.getMnemonic();
1670 if (mnemonic != newMnemonic) {
1671 int oldValue = mnemonic;
1672 mnemonic = newMnemonic;
1673 firePropertyChange(MNEMONIC_CHANGED_PROPERTY,
1674 oldValue, mnemonic);
1675 updateDisplayedMnemonicIndex(getText(), mnemonic);
1676 revalidate();
1677 repaint();
1678 }
1679 }
1680
1681 /**
1682 * Sets the amount of time (in milliseconds) required between
1683 * mouse press events for the button to generate the corresponding
1684 * action events. After the initial mouse press occurs (and action
1685 * event generated) any subsequent mouse press events which occur
1686 * on intervals less than the threshhold will be ignored and no
1687 * corresponding action event generated. By default the threshhold is 0,
1688 * which means that for each mouse press, an action event will be
1689 * fired, no matter how quickly the mouse clicks occur. In buttons
1690 * where this behavior is not desirable (for example, the "OK" button
1691 * in a dialog), this threshhold should be set to an appropriate
1692 * positive value.
1693 *
1694 * @see #getMultiClickThreshhold
1695 * @param threshhold the amount of time required between mouse
1696 * press events to generate corresponding action events
1697 * @exception IllegalArgumentException if threshhold < 0
1698 * @since 1.4
1699 */
1700 public void setMultiClickThreshhold(long threshhold) {
1701 if (threshhold < 0) {
1702 throw new IllegalArgumentException("threshhold must be >= 0");
1703 }
1704 this.multiClickThreshhold = threshhold;
1705 }
1706
1707 /**
1708 * Gets the amount of time (in milliseconds) required between
1709 * mouse press events for the button to generate the corresponding
1710 * action events.
1711 *
1712 * @see #setMultiClickThreshhold
1713 * @return the amount of time required between mouse press events
1714 * to generate corresponding action events
1715 * @since 1.4
1716 */
1717 public long getMultiClickThreshhold() {
1718 return multiClickThreshhold;
1719 }
1720
1721 /**
1722 * Returns the model that this button represents.
1723 * @return the <code>model</code> property
1724 * @see #setModel
1725 */
1726 public ButtonModel getModel() {
1727 return model;
1728 }
1729
1730 /**
1731 * Sets the model that this button represents.
1732 * @param newModel the new <code>ButtonModel</code>
1733 * @see #getModel
1734 * @beaninfo
1735 * bound: true
1736 * description: Model that the Button uses.
1737 */
1738 public void setModel(ButtonModel newModel) {
1739
1740 ButtonModel oldModel = getModel();
1741
1742 if (oldModel != null) {
1743 oldModel.removeChangeListener(changeListener);
1744 oldModel.removeActionListener(actionListener);
1745 oldModel.removeItemListener(itemListener);
1746 changeListener = null;
1747 actionListener = null;
1748 itemListener = null;
1749 }
1750
1751 model = newModel;
1752
1753 if (newModel != null) {
1754 changeListener = createChangeListener();
1755 actionListener = createActionListener();
1756 itemListener = createItemListener();
1757 newModel.addChangeListener(changeListener);
1758 newModel.addActionListener(actionListener);
1759 newModel.addItemListener(itemListener);
1760
1761 updateMnemonicProperties();
1762 //We invoke setEnabled() from JComponent
1763 //because setModel() can be called from a constructor
1764 //when the button is not fully initialized
1765 super.setEnabled(newModel.isEnabled());
1766
1767 } else {
1768 mnemonic = '\0';
1769 }
1770
1771 updateDisplayedMnemonicIndex(getText(), mnemonic);
1772
1773 firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
1774 if (newModel != oldModel) {
1775 revalidate();
1776 repaint();
1777 }
1778 }
1779
1780
1781 /**
1782 * Returns the L&F object that renders this component.
1783 * @return the ButtonUI object
1784 * @see #setUI
1785 */
1786 public ButtonUI getUI() {
1787 return (ButtonUI) ui;
1788 }
1789
1790
1791 /**
1792 * Sets the L&F object that renders this component.
1793 * @param ui the <code>ButtonUI</code> L&F object
1794 * @see #getUI
1795 * @beaninfo
1796 * bound: true
1797 * hidden: true
1798 * attribute: visualUpdate true
1799 * description: The UI object that implements the LookAndFeel.
1800 */
1801 public void setUI(ButtonUI ui) {
1802 super.setUI(ui);
1803 // disabled icons are generated by the LF so they should be unset here
1804 if (disabledIcon instanceof UIResource) {
1805 setDisabledIcon(null);
1806 }
1807 if (disabledSelectedIcon instanceof UIResource) {
1808 setDisabledSelectedIcon(null);
1809 }
1810 }
1811
1812
1813 /**
1814 * Resets the UI property to a value from the current look
1815 * and feel. Subtypes of <code>AbstractButton</code>
1816 * should override this to update the UI. For
1817 * example, <code>JButton</code> might do the following:
1818 * <pre>
1819 * setUI((ButtonUI)UIManager.getUI(
1820 * "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
1821 * </pre>
1822 */
1823 public void updateUI() {
1824 }
1825
1826 /**
1827 * Adds the specified component to this container at the specified
1828 * index, refer to
1829 * {@link java.awt.Container#addImpl(Component, Object, int)}
1830 * for a complete description of this method.
1831 *
1832 * @param comp the component to be added
1833 * @param constraints an object expressing layout constraints
1834 * for this component
1835 * @param index the position in the container's list at which to
1836 * insert the component, where <code>-1</code>
1837 * means append to the end
1838 * @exception IllegalArgumentException if <code>index</code> is invalid
1839 * @exception IllegalArgumentException if adding the container's parent
1840 * to itself
1841 * @exception IllegalArgumentException if adding a window to a container
1842 * @since 1.5
1843 */
1844 protected void addImpl(Component comp, Object constraints, int index) {
1845 if (!setLayout) {
1846 setLayout(new OverlayLayout(this));
1847 }
1848 super.addImpl(comp, constraints, index);
1849 }
1850
1851 /**
1852 * Sets the layout manager for this container, refer to
1853 * {@link java.awt.Container#setLayout(LayoutManager)}
1854 * for a complete description of this method.
1855 *
1856 * @param mgr the specified layout manager
1857 * @since 1.5
1858 */
1859 public void setLayout(LayoutManager mgr) {
1860 setLayout = true;
1861 super.setLayout(mgr);
1862 }
1863
1864 /**
1865 * Adds a <code>ChangeListener</code> to the button.
1866 * @param l the listener to be added
1867 */
1868 public void addChangeListener(ChangeListener l) {
1869 listenerList.add(ChangeListener.class, l);
1870 }
1871
1872 /**
1873 * Removes a ChangeListener from the button.
1874 * @param l the listener to be removed
1875 */
1876 public void removeChangeListener(ChangeListener l) {
1877 listenerList.remove(ChangeListener.class, l);
1878 }
1879
1880 /**
1881 * Returns an array of all the <code>ChangeListener</code>s added
1882 * to this AbstractButton with addChangeListener().
1883 *
1884 * @return all of the <code>ChangeListener</code>s added or an empty
1885 * array if no listeners have been added
1886 * @since 1.4
1887 */
1888 public ChangeListener[] getChangeListeners() {
1889 return (ChangeListener[])(listenerList.getListeners(
1890 ChangeListener.class));
1891 }
1892
1893 /**
1894 * Notifies all listeners that have registered interest for
1895 * notification on this event type. The event instance
1896 * is lazily created.
1897 * @see EventListenerList
1898 */
1899 protected void fireStateChanged() {
1900 // Guaranteed to return a non-null array
1901 Object[] listeners = listenerList.getListenerList();
1902 // Process the listeners last to first, notifying
1903 // those that are interested in this event
1904 for (int i = listeners.length-2; i>=0; i-=2) {
1905 if (listeners[i]==ChangeListener.class) {
1906 // Lazily create the event:
1907 if (changeEvent == null)
1908 changeEvent = new ChangeEvent(this);
1909 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
1910 }
1911 }
1912 }
1913
1914 /**
1915 * Adds an <code>ActionListener</code> to the button.
1916 * @param l the <code>ActionListener</code> to be added
1917 */
1918 public void addActionListener(ActionListener l) {
1919 listenerList.add(ActionListener.class, l);
1920 }
1921
1922 /**
1923 * Removes an <code>ActionListener</code> from the button.
1924 * If the listener is the currently set <code>Action</code>
1925 * for the button, then the <code>Action</code>
1926 * is set to <code>null</code>.
1927 *
1928 * @param l the listener to be removed
1929 */
1930 public void removeActionListener(ActionListener l) {
1931 if ((l != null) && (getAction() == l)) {
1932 setAction(null);
1933 } else {
1934 listenerList.remove(ActionListener.class, l);
1935 }
1936 }
1937
1938 /**
1939 * Returns an array of all the <code>ActionListener</code>s added
1940 * to this AbstractButton with addActionListener().
1941 *
1942 * @return all of the <code>ActionListener</code>s added or an empty
1943 * array if no listeners have been added
1944 * @since 1.4
1945 */
1946 public ActionListener[] getActionListeners() {
1947 return (ActionListener[])(listenerList.getListeners(
1948 ActionListener.class));
1949 }
1950
1951 /**
1952 * Subclasses that want to handle <code>ChangeEvents</code> differently
1953 * can override this to return another <code>ChangeListener</code>
1954 * implementation.
1955 *
1956 * @return the new <code>ChangeListener</code>
1957 */
1958 protected ChangeListener createChangeListener() {
1959 return getHandler();
1960 }
1961
1962 /**
1963 * Extends <code>ChangeListener</code> to be serializable.
1964 * <p>
1965 * <strong>Warning:</strong>
1966 * Serialized objects of this class will not be compatible with
1967 * future Swing releases. The current serialization support is
1968 * appropriate for short term storage or RMI between applications running
1969 * the same version of Swing. As of 1.4, support for long term storage
1970 * of all JavaBeans<sup><font size="-2">TM</font></sup>
1971 * has been added to the <code>java.beans</code> package.
1972 * Please see {@link java.beans.XMLEncoder}.
1973 */
1974 protected class ButtonChangeListener implements ChangeListener, Serializable {
1975 // NOTE: This class is NOT used, instead the functionality has
1976 // been moved to Handler.
1977 ButtonChangeListener() {
1978 }
1979
1980 public void stateChanged(ChangeEvent e) {
1981 getHandler().stateChanged(e);
1982 }
1983 }
1984
1985
1986 /**
1987 * Notifies all listeners that have registered interest for
1988 * notification on this event type. The event instance
1989 * is lazily created using the <code>event</code>
1990 * parameter.
1991 *
1992 * @param event the <code>ActionEvent</code> object
1993 * @see EventListenerList
1994 */
1995 protected void fireActionPerformed(ActionEvent event) {
1996 // Guaranteed to return a non-null array
1997 Object[] listeners = listenerList.getListenerList();
1998 ActionEvent e = null;
1999 // Process the listeners last to first, notifying
2000 // those that are interested in this event
2001 for (int i = listeners.length-2; i>=0; i-=2) {
2002 if (listeners[i]==ActionListener.class) {
2003 // Lazily create the event:
2004 if (e == null) {
2005 String actionCommand = event.getActionCommand();
2006 if(actionCommand == null) {
2007 actionCommand = getActionCommand();
2008 }
2009 e = new ActionEvent(AbstractButton.this,
2010 ActionEvent.ACTION_PERFORMED,
2011 actionCommand,
2012 event.getWhen(),
2013 event.getModifiers());
2014 }
2015 ((ActionListener)listeners[i+1]).actionPerformed(e);
2016 }
2017 }
2018 }
2019
2020 /**
2021 * Notifies all listeners that have registered interest for
2022 * notification on this event type. The event instance
2023 * is lazily created using the <code>event</code> parameter.
2024 *
2025 * @param event the <code>ItemEvent</code> object
2026 * @see EventListenerList
2027 */
2028 protected void fireItemStateChanged(ItemEvent event) {
2029 // Guaranteed to return a non-null array
2030 Object[] listeners = listenerList.getListenerList();
2031 ItemEvent e = null;
2032 // Process the listeners last to first, notifying
2033 // those that are interested in this event
2034 for (int i = listeners.length-2; i>=0; i-=2) {
2035 if (listeners[i]==ItemListener.class) {
2036 // Lazily create the event:
2037 if (e == null) {
2038 e = new ItemEvent(AbstractButton.this,
2039 ItemEvent.ITEM_STATE_CHANGED,
2040 AbstractButton.this,
2041 event.getStateChange());
2042 }
2043 ((ItemListener)listeners[i+1]).itemStateChanged(e);
2044 }
2045 }
2046 if (accessibleContext != null) {
2047 if (event.getStateChange() == ItemEvent.SELECTED) {
2048 accessibleContext.firePropertyChange(
2049 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2050 null, AccessibleState.SELECTED);
2051 accessibleContext.firePropertyChange(
2052 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
2053 Integer.valueOf(0), Integer.valueOf(1));
2054 } else {
2055 accessibleContext.firePropertyChange(
2056 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2057 AccessibleState.SELECTED, null);
2058 accessibleContext.firePropertyChange(
2059 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
2060 Integer.valueOf(1), Integer.valueOf(0));
2061 }
2062 }
2063 }
2064
2065
2066 protected ActionListener createActionListener() {
2067 return getHandler();
2068 }
2069
2070
2071 protected ItemListener createItemListener() {
2072 return getHandler();
2073 }
2074
2075
2076 /**
2077 * Enables (or disables) the button.
2078 * @param b true to enable the button, otherwise false
2079 */
2080 public void setEnabled(boolean b) {
2081 if (!b && model.isRollover()) {
2082 model.setRollover(false);
2083 }
2084 super.setEnabled(b);
2085 model.setEnabled(b);
2086 }
2087
2088 // *** Deprecated java.awt.Button APIs below *** //
2089
2090 /**
2091 * Returns the label text.
2092 *
2093 * @return a <code>String</code> containing the label
2094 * @deprecated - Replaced by <code>getText</code>
2095 */
2096 @Deprecated
2097 public String getLabel() {
2098 return getText();
2099 }
2100
2101 /**
2102 * Sets the label text.
2103 *
2104 * @param label a <code>String</code> containing the text
2105 * @deprecated - Replaced by <code>setText(text)</code>
2106 * @beaninfo
2107 * bound: true
2108 * description: Replace by setText(text)
2109 */
2110 @Deprecated
2111 public void setLabel(String label) {
2112 setText(label);
2113 }
2114
2115 /**
2116 * Adds an <code>ItemListener</code> to the <code>checkbox</code>.
2117 * @param l the <code>ItemListener</code> to be added
2118 */
2119 public void addItemListener(ItemListener l) {
2120 listenerList.add(ItemListener.class, l);
2121 }
2122
2123 /**
2124 * Removes an <code>ItemListener</code> from the button.
2125 * @param l the <code>ItemListener</code> to be removed
2126 */
2127 public void removeItemListener(ItemListener l) {
2128 listenerList.remove(ItemListener.class, l);
2129 }
2130
2131 /**
2132 * Returns an array of all the <code>ItemListener</code>s added
2133 * to this AbstractButton with addItemListener().
2134 *
2135 * @return all of the <code>ItemListener</code>s added or an empty
2136 * array if no listeners have been added
2137 * @since 1.4
2138 */
2139 public ItemListener[] getItemListeners() {
2140 return (ItemListener[])listenerList.getListeners(ItemListener.class);
2141 }
2142
2143 /**
2144 * Returns an array (length 1) containing the label or
2145 * <code>null</code> if the button is not selected.
2146 *
2147 * @return an array containing 1 Object: the text of the button,
2148 * if the item is selected; otherwise <code>null</code>
2149 */
2150 public Object[] getSelectedObjects() {
2151 if (isSelected() == false) {
2152 return null;
2153 }
2154 Object[] selectedObjects = new Object[1];
2155 selectedObjects[0] = getText();
2156 return selectedObjects;
2157 }
2158
2159 protected void init(String text, Icon icon) {
2160 if(text != null) {
2161 setText(text);
2162 }
2163
2164 if(icon != null) {
2165 setIcon(icon);
2166 }
2167
2168 // Set the UI
2169 updateUI();
2170
2171 setAlignmentX(LEFT_ALIGNMENT);
2172 setAlignmentY(CENTER_ALIGNMENT);
2173 }
2174
2175
2176 /**
2177 * This is overridden to return false if the current <code>Icon</code>'s
2178 * <code>Image</code> is not equal to the
2179 * passed in <code>Image</code> <code>img</code>.
2180 *
2181 * @param img the <code>Image</code> to be compared
2182 * @param infoflags flags used to repaint the button when the image
2183 * is updated and which determine how much is to be painted
2184 * @param x the x coordinate
2185 * @param y the y coordinate
2186 * @param w the width
2187 * @param h the height
2188 * @see java.awt.image.ImageObserver
2189 * @see java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
2190 */
2191 public boolean imageUpdate(Image img, int infoflags,
2192 int x, int y, int w, int h) {
2193 Icon iconDisplayed = getIcon();
2194 if (iconDisplayed == null) {
2195 return false;
2196 }
2197
2198 if (!model.isEnabled()) {
2199 if (model.isSelected()) {
2200 iconDisplayed = getDisabledSelectedIcon();
2201 } else {
2202 iconDisplayed = getDisabledIcon();
2203 }
2204 } else if (model.isPressed() && model.isArmed()) {
2205 iconDisplayed = getPressedIcon();
2206 } else if (isRolloverEnabled() && model.isRollover()) {
2207 if (model.isSelected()) {
2208 iconDisplayed = getRolloverSelectedIcon();
2209 } else {
2210 iconDisplayed = getRolloverIcon();
2211 }
2212 } else if (model.isSelected()) {
2213 iconDisplayed = getSelectedIcon();
2214 }
2215
2216 if (!SwingUtilities.doesIconReferenceImage(iconDisplayed, img)) {
2217 // We don't know about this image, disable the notification so
2218 // we don't keep repainting.
2219 return false;
2220 }
2221 return super.imageUpdate(img, infoflags, x, y, w, h);
2222 }
2223
2224 void setUIProperty(String propertyName, Object value) {
2225 if (propertyName == "borderPainted") {
2226 if (!borderPaintedSet) {
2227 setBorderPainted(((Boolean)value).booleanValue());
2228 borderPaintedSet = false;
2229 }
2230 } else if (propertyName == "rolloverEnabled") {
2231 if (!rolloverEnabledSet) {
2232 setRolloverEnabled(((Boolean)value).booleanValue());
2233 rolloverEnabledSet = false;
2234 }
2235 } else if (propertyName == "iconTextGap") {
2236 if (!iconTextGapSet) {
2237 setIconTextGap(((Number)value).intValue());
2238 iconTextGapSet = false;
2239 }
2240 } else if (propertyName == "contentAreaFilled") {
2241 if (!contentAreaFilledSet) {
2242 setContentAreaFilled(((Boolean)value).booleanValue());
2243 contentAreaFilledSet = false;
2244 }
2245 } else {
2246 super.setUIProperty(propertyName, value);
2247 }
2248 }
2249
2250 /**
2251 * Returns a string representation of this <code>AbstractButton</code>.
2252 * This method
2253 * is intended to be used only for debugging purposes, and the
2254 * content and format of the returned string may vary between
2255 * implementations. The returned string may be empty but may not
2256 * be <code>null</code>.
2257 * <P>
2258 * Overriding <code>paramString</code> to provide information about the
2259 * specific new aspects of the JFC components.
2260 *
2261 * @return a string representation of this <code>AbstractButton</code>
2262 */
2263 protected String paramString() {
2264 String defaultIconString = ((defaultIcon != null)
2265 && (defaultIcon != this) ?
2266 defaultIcon.toString() : "");
2267 String pressedIconString = ((pressedIcon != null)
2268 && (pressedIcon != this) ?
2269 pressedIcon.toString() : "");
2270 String disabledIconString = ((disabledIcon != null)
2271 && (disabledIcon != this) ?
2272 disabledIcon.toString() : "");
2273 String selectedIconString = ((selectedIcon != null)
2274 && (selectedIcon != this) ?
2275 selectedIcon.toString() : "");
2276 String disabledSelectedIconString = ((disabledSelectedIcon != null) &&
2277 (disabledSelectedIcon != this) ?
2278 disabledSelectedIcon.toString()
2279 : "");
2280 String rolloverIconString = ((rolloverIcon != null)
2281 && (rolloverIcon != this) ?
2282 rolloverIcon.toString() : "");
2283 String rolloverSelectedIconString = ((rolloverSelectedIcon != null) &&
2284 (rolloverSelectedIcon != this) ?
2285 rolloverSelectedIcon.toString()
2286 : "");
2287 String paintBorderString = (paintBorder ? "true" : "false");
2288 String paintFocusString = (paintFocus ? "true" : "false");
2289 String rolloverEnabledString = (rolloverEnabled ? "true" : "false");
2290
2291 return super.paramString() +
2292 ",defaultIcon=" + defaultIconString +
2293 ",disabledIcon=" + disabledIconString +
2294 ",disabledSelectedIcon=" + disabledSelectedIconString +
2295 ",margin=" + margin +
2296 ",paintBorder=" + paintBorderString +
2297 ",paintFocus=" + paintFocusString +
2298 ",pressedIcon=" + pressedIconString +
2299 ",rolloverEnabled=" + rolloverEnabledString +
2300 ",rolloverIcon=" + rolloverIconString +
2301 ",rolloverSelectedIcon=" + rolloverSelectedIconString +
2302 ",selectedIcon=" + selectedIconString +
2303 ",text=" + text;
2304 }
2305
2306
2307 private Handler getHandler() {
2308 if (handler == null) {
2309 handler = new Handler();
2310 }
2311 return handler;
2312 }
2313
2314
2315 //
2316 // Listeners that are added to model
2317 //
2318 class Handler implements ActionListener, ChangeListener, ItemListener,
2319 Serializable {
2320 //
2321 // ChangeListener
2322 //
2323 public void stateChanged(ChangeEvent e) {
2324 Object source = e.getSource();
2325
2326 updateMnemonicProperties();
2327 if (isEnabled() != model.isEnabled()) {
2328 setEnabled(model.isEnabled());
2329 }
2330 fireStateChanged();
2331 repaint();
2332 }
2333
2334 //
2335 // ActionListener
2336 //
2337 public void actionPerformed(ActionEvent event) {
2338 fireActionPerformed(event);
2339 }
2340
2341 //
2342 // ItemListener
2343 //
2344 public void itemStateChanged(ItemEvent event) {
2345 fireItemStateChanged(event);
2346 if (shouldUpdateSelectedStateFromAction()) {
2347 Action action = getAction();
2348 if (action != null && AbstractAction.hasSelectedKey(action)) {
2349 boolean selected = isSelected();
2350 boolean isActionSelected = AbstractAction.isSelected(
2351 action);
2352 if (isActionSelected != selected) {
2353 action.putValue(Action.SELECTED_KEY, selected);
2354 }
2355 }
2356 }
2357 }
2358 }
2359
2360 ///////////////////
2361 // Accessibility support
2362 ///////////////////
2363 /**
2364 * This class implements accessibility support for the
2365 * <code>AbstractButton</code> class. It provides an implementation of the
2366 * Java Accessibility API appropriate to button and menu item
2367 * user-interface elements.
2368 * <p>
2369 * <strong>Warning:</strong>
2370 * Serialized objects of this class will not be compatible with
2371 * future Swing releases. The current serialization support is
2372 * appropriate for short term storage or RMI between applications running
2373 * the same version of Swing. As of 1.4, support for long term storage
2374 * of all JavaBeans<sup><font size="-2">TM</font></sup>
2375 * has been added to the <code>java.beans</code> package.
2376 * Please see {@link java.beans.XMLEncoder}.
2377 * @since 1.4
2378 */
2379 protected abstract class AccessibleAbstractButton
2380 extends AccessibleJComponent implements AccessibleAction,
2381 AccessibleValue, AccessibleText, AccessibleExtendedComponent {
2382
2383 /**
2384 * Returns the accessible name of this object.
2385 *
2386 * @return the localized name of the object -- can be
2387 * <code>null</code> if this
2388 * object does not have a name
2389 */
2390 public String getAccessibleName() {
2391 String name = accessibleName;
2392
2393 if (name == null) {
2394 name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
2395 }
2396 if (name == null) {
2397 name = AbstractButton.this.getText();
2398 }
2399 if (name == null) {
2400 name = super.getAccessibleName();
2401 }
2402 return name;
2403 }
2404
2405 /**
2406 * Get the AccessibleIcons associated with this object if one
2407 * or more exist. Otherwise return null.
2408 * @since 1.3
2409 */
2410 public AccessibleIcon [] getAccessibleIcon() {
2411 Icon defaultIcon = getIcon();
2412
2413 if (defaultIcon instanceof Accessible) {
2414 AccessibleContext ac =
2415 ((Accessible)defaultIcon).getAccessibleContext();
2416 if (ac != null && ac instanceof AccessibleIcon) {
2417 return new AccessibleIcon[] { (AccessibleIcon)ac };
2418 }
2419 }
2420 return null;
2421 }
2422
2423 /**
2424 * Get the state set of this object.
2425 *
2426 * @return an instance of AccessibleState containing the current state
2427 * of the object
2428 * @see AccessibleState
2429 */
2430 public AccessibleStateSet getAccessibleStateSet() {
2431 AccessibleStateSet states = super.get