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://docs.oracle.com/javase/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://docs.oracle.com/javase/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™
70 * has been added to the <code>java.beans</code> package.
71 * Please see {@link java.beans.XMLEncoder}.
72 *
73 * @author Jeff Dinkins
74 * @since 1.2
75 */
76 @SuppressWarnings("serial") // Same-version serialization only
77 public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {
78
79 // *********************************
80 // ******* Button properties *******
81 // *********************************
82
83 /** Identifies a change in the button model. */
84 public static final String MODEL_CHANGED_PROPERTY = "model";
85 /** Identifies a change in the button's text. */
86 public static final String TEXT_CHANGED_PROPERTY = "text";
87 /** Identifies a change to the button's mnemonic. */
88 public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
89
90 // Text positioning and alignment
199 private long multiClickThreshhold = 0;
200
201 private boolean borderPaintedSet = false;
202 private boolean rolloverEnabledSet = false;
203 private boolean iconTextGapSet = false;
204 private boolean contentAreaFilledSet = false;
205
206 // Whether or not we've set the LayoutManager.
207 private boolean setLayout = false;
208
209 // This is only used by JButton, promoted to avoid an extra
210 // boolean field in JButton
211 boolean defaultCapable = true;
212
213 /**
214 * Combined listeners: ActionListener, ChangeListener, ItemListener.
215 */
216 private Handler handler;
217
218 /**
219 * The button model's <code>changeListener</code>.
220 */
221 protected ChangeListener changeListener = null;
222 /**
223 * The button model's <code>ActionListener</code>.
224 */
225 protected ActionListener actionListener = null;
226 /**
227 * The button model's <code>ItemListener</code>.
228 */
229 protected ItemListener itemListener = null;
230
231 /**
232 * Only one <code>ChangeEvent</code> is needed per button
233 * instance since the
234 * event's only state is the source property. The source of events
235 * generated is always "this".
236 */
237 protected transient ChangeEvent changeEvent;
238
239 private boolean hideActionText = false;
240
241 /**
242 * Sets the <code>hideActionText</code> property, which determines
243 * whether the button displays text from the <code>Action</code>.
244 * This is useful only if an <code>Action</code> has been
245 * installed on the button.
246 *
247 * @param hideActionText <code>true</code> if the button's
248 * <code>text</code> property should not reflect
249 * that of the <code>Action</code>; the default is
250 * <code>false</code>
251 * @see <a href="Action.html#buttonActions">Swing Components Supporting
252 * <code>Action</code></a>
253 * @since 1.6
254 * @beaninfo
255 * bound: true
256 * expert: true
257 * description: Whether the text of the button should come from
258 * the <code>Action</code>.
259 */
260 public void setHideActionText(boolean hideActionText) {
261 if (hideActionText != this.hideActionText) {
262 this.hideActionText = hideActionText;
263 if (getAction() != null) {
264 setTextFromAction(getAction(), false);
265 }
266 firePropertyChange("hideActionText", !hideActionText,
267 hideActionText);
268 }
269 }
270
271 /**
272 * Returns the value of the <code>hideActionText</code> property, which
273 * determines whether the button displays text from the
274 * <code>Action</code>. This is useful only if an <code>Action</code>
275 * has been installed on the button.
276 *
277 * @return <code>true</code> if the button's <code>text</code>
278 * property should not reflect that of the
279 * <code>Action</code>; the default is <code>false</code>
280 * @since 1.6
281 */
282 public boolean getHideActionText() {
283 return hideActionText;
284 }
285
286 /**
287 * Returns the button's text.
288 * @return the buttons text
289 * @see #setText
290 */
291 public String getText() {
292 return text;
293 }
294
295 /**
296 * Sets the button's text.
297 * @param text the string used to set the text
298 * @see #getText
299 * @beaninfo
314 oldValue, text);
315 }
316 if (text == null || oldValue == null || !text.equals(oldValue)) {
317 revalidate();
318 repaint();
319 }
320 }
321
322
323 /**
324 * Returns the state of the button. True if the
325 * toggle button is selected, false if it's not.
326 * @return true if the toggle button is selected, otherwise false
327 */
328 public boolean isSelected() {
329 return model.isSelected();
330 }
331
332 /**
333 * Sets the state of the button. Note that this method does not
334 * trigger an <code>actionEvent</code>.
335 * Call <code>doClick</code> to perform a programmatic action change.
336 *
337 * @param b true if the button is selected, otherwise false
338 */
339 public void setSelected(boolean b) {
340 boolean oldValue = isSelected();
341
342 // TIGER - 4840653
343 // Removed code which fired an AccessibleState.SELECTED
344 // PropertyChangeEvent since this resulted in two
345 // identical events being fired since
346 // AbstractButton.fireItemStateChanged also fires the
347 // same event. This caused screen readers to speak the
348 // name of the item twice.
349
350 model.setSelected(b);
351 }
352
353 /**
354 * Programmatically perform a "click". This does the same
355 * thing as if the user had pressed and released the button.
356 */
357 public void doClick() {
358 doClick(68);
359 }
360
361 /**
362 * Programmatically perform a "click". This does the same
363 * thing as if the user had pressed and released the button.
364 * The button stays visually "pressed" for <code>pressTime</code>
365 * milliseconds.
366 *
367 * @param pressTime the time to "hold down" the button, in milliseconds
368 */
369 public void doClick(int pressTime) {
370 Dimension size = getSize();
371 model.setArmed(true);
372 model.setPressed(true);
373 paintImmediately(new Rectangle(0,0, size.width, size.height));
374 try {
375 Thread.sleep(pressTime);
376 } catch(InterruptedException ie) {
377 }
378 model.setPressed(false);
379 model.setArmed(false);
380 }
381
382 /**
383 * Sets space for margin between the button's border and
384 * the label. Setting to <code>null</code> will cause the button to
385 * use the default margin. The button's default <code>Border</code>
386 * object will use this value to create the proper margin.
387 * However, if a non-default border is set on the button,
388 * it is that <code>Border</code> object's responsibility to create the
389 * appropriate margin space (else this property will
390 * effectively be ignored).
391 *
392 * @param m the space between the border and the label
393 *
394 * @beaninfo
395 * bound: true
396 * attribute: visualUpdate true
397 * description: The space between the button's border and the label.
398 */
399 public void setMargin(Insets m) {
400 // Cache the old margin if it comes from the UI
401 if(m instanceof UIResource) {
402 defaultMargin = m;
403 } else if(margin instanceof UIResource) {
404 defaultMargin = margin;
405 }
406
407 // If the client passes in a null insets, restore the margin
408 // from the UI if possible
409 if(m == null && defaultMargin != null) {
410 m = defaultMargin;
411 }
412
413 Insets old = margin;
414 margin = m;
415 firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
416 if (old == null || !old.equals(m)) {
417 revalidate();
418 repaint();
419 }
420 }
421
422 /**
423 * Returns the margin between the button's border and
424 * the label.
425 *
426 * @return an <code>Insets</code> object specifying the margin
427 * between the botton's border and the label
428 * @see #setMargin
429 */
430 public Insets getMargin() {
431 return (margin == null) ? null : (Insets) margin.clone();
432 }
433
434 /**
435 * Returns the default icon.
436 * @return the default <code>Icon</code>
437 * @see #setIcon
438 */
439 public Icon getIcon() {
440 return defaultIcon;
441 }
442
443 /**
444 * Sets the button's default icon. This icon is
445 * also used as the "pressed" and "disabled" icon if
446 * there is no explicitly set pressed icon.
447 *
448 * @param defaultIcon the icon used as the default image
449 * @see #getIcon
450 * @see #setPressedIcon
451 * @beaninfo
452 * bound: true
453 * attribute: visualUpdate true
454 * description: The button's default icon
455 */
456 public void setIcon(Icon defaultIcon) {
467 }
468
469 firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
470 if (accessibleContext != null) {
471 accessibleContext.firePropertyChange(
472 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
473 oldValue, defaultIcon);
474 }
475 if (defaultIcon != oldValue) {
476 if (defaultIcon == null || oldValue == null ||
477 defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
478 defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
479 revalidate();
480 }
481 repaint();
482 }
483 }
484
485 /**
486 * Returns the pressed icon for the button.
487 * @return the <code>pressedIcon</code> property
488 * @see #setPressedIcon
489 */
490 public Icon getPressedIcon() {
491 return pressedIcon;
492 }
493
494 /**
495 * Sets the pressed icon for the button.
496 * @param pressedIcon the icon used as the "pressed" image
497 * @see #getPressedIcon
498 * @beaninfo
499 * bound: true
500 * attribute: visualUpdate true
501 * description: The pressed icon for the button.
502 */
503 public void setPressedIcon(Icon pressedIcon) {
504 Icon oldValue = this.pressedIcon;
505 this.pressedIcon = pressedIcon;
506 firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
507 if (accessibleContext != null) {
508 accessibleContext.firePropertyChange(
509 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
510 oldValue, pressedIcon);
511 }
512 if (pressedIcon != oldValue) {
513 if (getModel().isPressed()) {
514 repaint();
515 }
516 }
517 }
518
519 /**
520 * Returns the selected icon for the button.
521 * @return the <code>selectedIcon</code> property
522 * @see #setSelectedIcon
523 */
524 public Icon getSelectedIcon() {
525 return selectedIcon;
526 }
527
528 /**
529 * Sets the selected icon for the button.
530 * @param selectedIcon the icon used as the "selected" image
531 * @see #getSelectedIcon
532 * @beaninfo
533 * bound: true
534 * attribute: visualUpdate true
535 * description: The selected icon for the button.
536 */
537 public void setSelectedIcon(Icon selectedIcon) {
538 Icon oldValue = this.selectedIcon;
539 this.selectedIcon = selectedIcon;
540
541 /* If the default selected icon has really changed and we had
547 disabledSelectedIcon instanceof UIResource) {
548
549 disabledSelectedIcon = null;
550 }
551
552 firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
553 if (accessibleContext != null) {
554 accessibleContext.firePropertyChange(
555 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
556 oldValue, selectedIcon);
557 }
558 if (selectedIcon != oldValue) {
559 if (isSelected()) {
560 repaint();
561 }
562 }
563 }
564
565 /**
566 * Returns the rollover icon for the button.
567 * @return the <code>rolloverIcon</code> property
568 * @see #setRolloverIcon
569 */
570 public Icon getRolloverIcon() {
571 return rolloverIcon;
572 }
573
574 /**
575 * Sets the rollover icon for the button.
576 * @param rolloverIcon the icon used as the "rollover" image
577 * @see #getRolloverIcon
578 * @beaninfo
579 * bound: true
580 * attribute: visualUpdate true
581 * description: The rollover icon for the button.
582 */
583 public void setRolloverIcon(Icon rolloverIcon) {
584 Icon oldValue = this.rolloverIcon;
585 this.rolloverIcon = rolloverIcon;
586 firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
587 if (accessibleContext != null) {
588 accessibleContext.firePropertyChange(
589 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
590 oldValue, rolloverIcon);
591 }
592 setRolloverEnabled(true);
593 if (rolloverIcon != oldValue) {
594 // No way to determine whether we are currently in
595 // a rollover state, so repaint regardless
596 repaint();
597 }
598
599 }
600
601 /**
602 * Returns the rollover selection icon for the button.
603 * @return the <code>rolloverSelectedIcon</code> property
604 * @see #setRolloverSelectedIcon
605 */
606 public Icon getRolloverSelectedIcon() {
607 return rolloverSelectedIcon;
608 }
609
610 /**
611 * Sets the rollover selected icon for the button.
612 * @param rolloverSelectedIcon the icon used as the
613 * "selected rollover" image
614 * @see #getRolloverSelectedIcon
615 * @beaninfo
616 * bound: true
617 * attribute: visualUpdate true
618 * description: The rollover selected icon for the button.
619 */
620 public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
621 Icon oldValue = this.rolloverSelectedIcon;
622 this.rolloverSelectedIcon = rolloverSelectedIcon;
623 firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
627 oldValue, rolloverSelectedIcon);
628 }
629 setRolloverEnabled(true);
630 if (rolloverSelectedIcon != oldValue) {
631 // No way to determine whether we are currently in
632 // a rollover state, so repaint regardless
633 if (isSelected()) {
634 repaint();
635 }
636 }
637 }
638
639 /**
640 * Returns the icon used by the button when it's disabled.
641 * If no disabled icon has been set this will forward the call to
642 * the look and feel to construct an appropriate disabled Icon.
643 * <p>
644 * Some look and feels might not render the disabled Icon, in which
645 * case they will ignore this.
646 *
647 * @return the <code>disabledIcon</code> property
648 * @see #getPressedIcon
649 * @see #setDisabledIcon
650 * @see javax.swing.LookAndFeel#getDisabledIcon
651 */
652 @Transient
653 public Icon getDisabledIcon() {
654 if (disabledIcon == null) {
655 disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
656 if (disabledIcon != null) {
657 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);
658 }
659 }
660 return disabledIcon;
661 }
662
663 /**
664 * Sets the disabled icon for the button.
665 * @param disabledIcon the icon used as the disabled image
666 * @see #getDisabledIcon
667 * @beaninfo
673 Icon oldValue = this.disabledIcon;
674 this.disabledIcon = disabledIcon;
675 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
676 if (accessibleContext != null) {
677 accessibleContext.firePropertyChange(
678 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
679 oldValue, disabledIcon);
680 }
681 if (disabledIcon != oldValue) {
682 if (!isEnabled()) {
683 repaint();
684 }
685 }
686 }
687
688 /**
689 * Returns the icon used by the button when it's disabled and selected.
690 * If no disabled selection icon has been set, this will forward
691 * the call to the LookAndFeel to construct an appropriate disabled
692 * Icon from the selection icon if it has been set and to
693 * <code>getDisabledIcon()</code> otherwise.
694 * <p>
695 * Some look and feels might not render the disabled selected Icon, in
696 * which case they will ignore this.
697 *
698 * @return the <code>disabledSelectedIcon</code> property
699 * @see #getDisabledIcon
700 * @see #setDisabledSelectedIcon
701 * @see javax.swing.LookAndFeel#getDisabledSelectedIcon
702 */
703 public Icon getDisabledSelectedIcon() {
704 if (disabledSelectedIcon == null) {
705 if (selectedIcon != null) {
706 disabledSelectedIcon = UIManager.getLookAndFeel().
707 getDisabledSelectedIcon(this, getSelectedIcon());
708 } else {
709 return getDisabledIcon();
710 }
711 }
712 return disabledSelectedIcon;
713 }
714
715 /**
716 * Sets the disabled selection icon for the button.
717 * @param disabledSelectedIcon the icon used as the disabled
718 * selection image
729 if (accessibleContext != null) {
730 accessibleContext.firePropertyChange(
731 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
732 oldValue, disabledSelectedIcon);
733 }
734 if (disabledSelectedIcon != oldValue) {
735 if (disabledSelectedIcon == null || oldValue == null ||
736 disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
737 disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
738 revalidate();
739 }
740 if (!isEnabled() && isSelected()) {
741 repaint();
742 }
743 }
744 }
745
746 /**
747 * Returns the vertical alignment of the text and icon.
748 *
749 * @return the <code>verticalAlignment</code> property, one of the
750 * following values:
751 * <ul>
752 * <li>{@code SwingConstants.CENTER} (the default)
753 * <li>{@code SwingConstants.TOP}
754 * <li>{@code SwingConstants.BOTTOM}
755 * </ul>
756 */
757 public int getVerticalAlignment() {
758 return verticalAlignment;
759 }
760
761 /**
762 * Sets the vertical alignment of the icon and text.
763 * @param alignment one of the following values:
764 * <ul>
765 * <li>{@code SwingConstants.CENTER} (the default)
766 * <li>{@code SwingConstants.TOP}
767 * <li>{@code SwingConstants.BOTTOM}
768 * </ul>
769 * @throws IllegalArgumentException if the alignment is not one of the legal
771 * @beaninfo
772 * bound: true
773 * enum: TOP SwingConstants.TOP
774 * CENTER SwingConstants.CENTER
775 * BOTTOM SwingConstants.BOTTOM
776 * attribute: visualUpdate true
777 * description: The vertical alignment of the icon and text.
778 */
779 public void setVerticalAlignment(int alignment) {
780 if (alignment == verticalAlignment) return;
781 int oldValue = verticalAlignment;
782 verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
783 firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment); repaint();
784 }
785
786 /**
787 * Returns the horizontal alignment of the icon and text.
788 * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
789 * but subclasses such as {@code JCheckBox} may use a different default.
790 *
791 * @return the <code>horizontalAlignment</code> property,
792 * one of the following values:
793 * <ul>
794 * <li>{@code SwingConstants.RIGHT}
795 * <li>{@code SwingConstants.LEFT}
796 * <li>{@code SwingConstants.CENTER}
797 * <li>{@code SwingConstants.LEADING}
798 * <li>{@code SwingConstants.TRAILING}
799 * </ul>
800 */
801 public int getHorizontalAlignment() {
802 return horizontalAlignment;
803 }
804
805 /**
806 * Sets the horizontal alignment of the icon and text.
807 * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
808 * but subclasses such as {@code JCheckBox} may use a different default.
809 *
810 * @param alignment the alignment value, one of the following values:
811 * <ul>
823 * CENTER SwingConstants.CENTER
824 * RIGHT SwingConstants.RIGHT
825 * LEADING SwingConstants.LEADING
826 * TRAILING SwingConstants.TRAILING
827 * attribute: visualUpdate true
828 * description: The horizontal alignment of the icon and text.
829 */
830 public void setHorizontalAlignment(int alignment) {
831 if (alignment == horizontalAlignment) return;
832 int oldValue = horizontalAlignment;
833 horizontalAlignment = checkHorizontalKey(alignment,
834 "horizontalAlignment");
835 firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
836 oldValue, horizontalAlignment);
837 repaint();
838 }
839
840
841 /**
842 * Returns the vertical position of the text relative to the icon.
843 * @return the <code>verticalTextPosition</code> property,
844 * one of the following values:
845 * <ul>
846 * <li>{@code SwingConstants.CENTER} (the default)
847 * <li>{@code SwingConstants.TOP}
848 * <li>{@code SwingConstants.BOTTOM}
849 * </ul>
850 */
851 public int getVerticalTextPosition() {
852 return verticalTextPosition;
853 }
854
855 /**
856 * Sets the vertical position of the text relative to the icon.
857 * @param textPosition one of the following values:
858 * <ul>
859 * <li>{@code SwingConstants.CENTER} (the default)
860 * <li>{@code SwingConstants.TOP}
861 * <li>{@code SwingConstants.BOTTOM}
862 * </ul>
863 * @beaninfo
864 * bound: true
865 * enum: TOP SwingConstants.TOP
866 * CENTER SwingConstants.CENTER
867 * BOTTOM SwingConstants.BOTTOM
868 * attribute: visualUpdate true
869 * description: The vertical position of the text relative to the icon.
870 */
871 public void setVerticalTextPosition(int textPosition) {
872 if (textPosition == verticalTextPosition) return;
873 int oldValue = verticalTextPosition;
874 verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
875 firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
876 revalidate();
877 repaint();
878 }
879
880 /**
881 * Returns the horizontal position of the text relative to the icon.
882 * @return the <code>horizontalTextPosition</code> property,
883 * one of the following values:
884 * <ul>
885 * <li>{@code SwingConstants.RIGHT}
886 * <li>{@code SwingConstants.LEFT}
887 * <li>{@code SwingConstants.CENTER}
888 * <li>{@code SwingConstants.LEADING}
889 * <li>{@code SwingConstants.TRAILING} (the default)
890 * </ul>
891 */
892 public int getHorizontalTextPosition() {
893 return horizontalTextPosition;
894 }
895
896 /**
897 * Sets the horizontal position of the text relative to the icon.
898 * @param textPosition one of the following values:
899 * <ul>
900 * <li>{@code SwingConstants.RIGHT}
901 * <li>{@code SwingConstants.LEFT}
902 * <li>{@code SwingConstants.CENTER}
903 * <li>{@code SwingConstants.LEADING}
904 * <li>{@code SwingConstants.TRAILING} (the default)
905 * </ul>
906 * @exception IllegalArgumentException if <code>textPosition</code>
907 * is not one of the legal values listed above
908 * @beaninfo
909 * bound: true
910 * enum: LEFT SwingConstants.LEFT
911 * CENTER SwingConstants.CENTER
912 * RIGHT SwingConstants.RIGHT
913 * LEADING SwingConstants.LEADING
914 * TRAILING SwingConstants.TRAILING
915 * attribute: visualUpdate true
916 * description: The horizontal position of the text relative to the icon.
917 */
918 public void setHorizontalTextPosition(int textPosition) {
919 if (textPosition == horizontalTextPosition) return;
920 int oldValue = horizontalTextPosition;
921 horizontalTextPosition = checkHorizontalKey(textPosition,
922 "horizontalTextPosition");
923 firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
924 oldValue,
925 horizontalTextPosition);
926 revalidate();
1046 public void setActionCommand(String actionCommand) {
1047 getModel().setActionCommand(actionCommand);
1048 }
1049
1050 /**
1051 * Returns the action command for this button.
1052 * @return the action command for this button
1053 */
1054 public String getActionCommand() {
1055 String ac = getModel().getActionCommand();
1056 if(ac == null) {
1057 ac = getText();
1058 }
1059 return ac;
1060 }
1061
1062 private Action action;
1063 private PropertyChangeListener actionPropertyChangeListener;
1064
1065 /**
1066 * Sets the <code>Action</code>.
1067 * The new <code>Action</code> replaces any previously set
1068 * <code>Action</code> but does not affect <code>ActionListeners</code>
1069 * independently added with <code>addActionListener</code>.
1070 * If the <code>Action</code> is already a registered
1071 * <code>ActionListener</code> for the button, it is not re-registered.
1072 * <p>
1073 * Setting the <code>Action</code> results in immediately changing
1074 * all the properties described in <a href="Action.html#buttonActions">
1075 * Swing Components Supporting <code>Action</code></a>.
1076 * Subsequently, the button's properties are automatically updated
1077 * as the <code>Action</code>'s properties change.
1078 * <p>
1079 * This method uses three other methods to set
1080 * and help track the <code>Action</code>'s property values.
1081 * It uses the <code>configurePropertiesFromAction</code> method
1082 * to immediately change the button's properties.
1083 * To track changes in the <code>Action</code>'s property values,
1084 * this method registers the <code>PropertyChangeListener</code>
1085 * returned by <code>createActionPropertyChangeListener</code>. The
1086 * default {@code PropertyChangeListener} invokes the
1087 * {@code actionPropertyChanged} method when a property in the
1088 * {@code Action} changes.
1089 *
1090 * @param a the <code>Action</code> for the <code>AbstractButton</code>,
1091 * or <code>null</code>
1092 * @since 1.3
1093 * @see Action
1094 * @see #getAction
1095 * @see #configurePropertiesFromAction
1096 * @see #createActionPropertyChangeListener
1097 * @see #actionPropertyChanged
1098 * @beaninfo
1099 * bound: true
1100 * attribute: visualUpdate true
1101 * description: the Action instance connected with this ActionEvent source
1102 */
1103 public void setAction(Action a) {
1104 Action oldValue = getAction();
1105 if (action==null || !action.equals(a)) {
1106 action = a;
1107 if (oldValue!=null) {
1108 removeActionListener(oldValue);
1109 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1110 actionPropertyChangeListener = null;
1111 }
1118 // Reverse linkage:
1119 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1120 action.addPropertyChangeListener(actionPropertyChangeListener);
1121 }
1122 firePropertyChange("action", oldValue, action);
1123 }
1124 }
1125
1126 private boolean isListener(Class<?> c, ActionListener a) {
1127 boolean isListener = false;
1128 Object[] listeners = listenerList.getListenerList();
1129 for (int i = listeners.length-2; i>=0; i-=2) {
1130 if (listeners[i]==c && listeners[i+1]==a) {
1131 isListener=true;
1132 }
1133 }
1134 return isListener;
1135 }
1136
1137 /**
1138 * Returns the currently set <code>Action</code> for this
1139 * <code>ActionEvent</code> source, or <code>null</code>
1140 * if no <code>Action</code> is set.
1141 *
1142 * @return the <code>Action</code> for this <code>ActionEvent</code>
1143 * source, or <code>null</code>
1144 * @since 1.3
1145 * @see Action
1146 * @see #setAction
1147 */
1148 public Action getAction() {
1149 return action;
1150 }
1151
1152 /**
1153 * Sets the properties on this button to match those in the specified
1154 * <code>Action</code>. Refer to <a href="Action.html#buttonActions">
1155 * Swing Components Supporting <code>Action</code></a> for more
1156 * details as to which properties this sets.
1157 *
1158 * @param a the <code>Action</code> from which to get the properties,
1159 * or <code>null</code>
1160 * @since 1.3
1161 * @see Action
1162 * @see #setAction
1163 */
1164 protected void configurePropertiesFromAction(Action a) {
1165 setMnemonicFromAction(a);
1166 setTextFromAction(a, false);
1167 AbstractAction.setToolTipTextFromAction(this, a);
1168 setIconFromAction(a);
1169 setActionCommandFromAction(a);
1170 AbstractAction.setEnabledFromAction(this, a);
1171 if (AbstractAction.hasSelectedKey(a) &&
1172 shouldUpdateSelectedStateFromAction()) {
1173 setSelectedFromAction(a);
1174 }
1175 setDisplayedMnemonicIndexFromAction(a, false);
1176 }
1177
1178 void clientPropertyChanged(Object key, Object oldValue,
1179 Object newValue) {
1188
1189 /**
1190 * Button subclasses that support mirroring the selected state from
1191 * the action should override this to return true. AbstractButton's
1192 * implementation returns false.
1193 */
1194 boolean shouldUpdateSelectedStateFromAction() {
1195 return false;
1196 }
1197
1198 /**
1199 * Updates the button's state in response to property changes in the
1200 * associated action. This method is invoked from the
1201 * {@code PropertyChangeListener} returned from
1202 * {@code createActionPropertyChangeListener}. Subclasses do not normally
1203 * need to invoke this. Subclasses that support additional {@code Action}
1204 * properties should override this and
1205 * {@code configurePropertiesFromAction}.
1206 * <p>
1207 * Refer to the table at <a href="Action.html#buttonActions">
1208 * Swing Components Supporting <code>Action</code></a> for a list of
1209 * the properties this method sets.
1210 *
1211 * @param action the <code>Action</code> associated with this button
1212 * @param propertyName the name of the property that changed
1213 * @since 1.6
1214 * @see Action
1215 * @see #configurePropertiesFromAction
1216 */
1217 protected void actionPropertyChanged(Action action, String propertyName) {
1218 if (propertyName == Action.NAME) {
1219 setTextFromAction(action, true);
1220 } else if (propertyName == "enabled") {
1221 AbstractAction.setEnabledFromAction(this, action);
1222 } else if (propertyName == Action.SHORT_DESCRIPTION) {
1223 AbstractAction.setToolTipTextFromAction(this, action);
1224 } else if (propertyName == Action.SMALL_ICON) {
1225 smallIconChanged(action);
1226 } else if (propertyName == Action.MNEMONIC_KEY) {
1227 setMnemonicFromAction(action);
1228 } else if (propertyName == Action.ACTION_COMMAND_KEY) {
1229 setActionCommandFromAction(action);
1230 } else if (propertyName == Action.SELECTED_KEY &&
1231 AbstractAction.hasSelectedKey(action) &&
1313 if (a != null) {
1314 selected = AbstractAction.isSelected(a);
1315 }
1316 if (selected != isSelected()) {
1317 // This won't notify ActionListeners, but that should be
1318 // ok as the change is coming from the Action.
1319 setSelected(selected);
1320 // Make sure the change actually took effect
1321 if (!selected && isSelected()) {
1322 if (getModel() instanceof DefaultButtonModel) {
1323 ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup();
1324 if (group != null) {
1325 group.clearSelection();
1326 }
1327 }
1328 }
1329 }
1330 }
1331
1332 /**
1333 * Creates and returns a <code>PropertyChangeListener</code> that is
1334 * responsible for listening for changes from the specified
1335 * <code>Action</code> and updating the appropriate properties.
1336 * <p>
1337 * <b>Warning:</b> If you subclass this do not create an anonymous
1338 * inner class. If you do the lifetime of the button will be tied to
1339 * that of the <code>Action</code>.
1340 *
1341 * @param a the button's action
1342 * @return the {@code PropertyChangeListener}
1343 * @since 1.3
1344 * @see Action
1345 * @see #setAction
1346 */
1347 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1348 return createActionPropertyChangeListener0(a);
1349 }
1350
1351
1352 PropertyChangeListener createActionPropertyChangeListener0(Action a) {
1353 return new ButtonActionPropertyChangeListener(this, a);
1354 }
1355
1356 @SuppressWarnings("serial")
1357 private static class ButtonActionPropertyChangeListener
1358 extends ActionPropertyChangeListener<AbstractButton> {
1359 ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
1360 super(b, a);
1361 }
1362 protected void actionPropertyChanged(AbstractButton button,
1363 Action action,
1364 PropertyChangeEvent e) {
1365 if (AbstractAction.shouldReconfigure(e)) {
1366 button.configurePropertiesFromAction(action);
1367 } else {
1368 button.actionPropertyChanged(action, e.getPropertyName());
1369 }
1370 }
1371 }
1372
1373 /**
1374 * Gets the <code>borderPainted</code> property.
1375 *
1376 * @return the value of the <code>borderPainted</code> property
1377 * @see #setBorderPainted
1378 */
1379 public boolean isBorderPainted() {
1380 return paintBorder;
1381 }
1382
1383 /**
1384 * Sets the <code>borderPainted</code> property.
1385 * If <code>true</code> and the button has a border,
1386 * the border is painted. The default value for the
1387 * <code>borderPainted</code> property is <code>true</code>.
1388 * <p>
1389 * Some look and feels might not support
1390 * the <code>borderPainted</code> property,
1391 * in which case they ignore this.
1392 *
1393 * @param b if true and border property is not <code>null</code>,
1394 * the border is painted
1395 * @see #isBorderPainted
1396 * @beaninfo
1397 * bound: true
1398 * attribute: visualUpdate true
1399 * description: Whether the border should be painted.
1400 */
1401 public void setBorderPainted(boolean b) {
1402 boolean oldValue = paintBorder;
1403 paintBorder = b;
1404 borderPaintedSet = true;
1405 firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
1406 if (b != oldValue) {
1407 revalidate();
1408 repaint();
1409 }
1410 }
1411
1412 /**
1413 * Paint the button's border if <code>BorderPainted</code>
1414 * property is true and the button has a border.
1415 * @param g the <code>Graphics</code> context in which to paint
1416 *
1417 * @see #paint
1418 * @see #setBorder
1419 */
1420 protected void paintBorder(Graphics g) {
1421 if (isBorderPainted()) {
1422 super.paintBorder(g);
1423 }
1424 }
1425
1426 /**
1427 * Gets the <code>paintFocus</code> property.
1428 *
1429 * @return the <code>paintFocus</code> property
1430 * @see #setFocusPainted
1431 */
1432 public boolean isFocusPainted() {
1433 return paintFocus;
1434 }
1435
1436 /**
1437 * Sets the <code>paintFocus</code> property, which must
1438 * be <code>true</code> for the focus state to be painted.
1439 * The default value for the <code>paintFocus</code> property
1440 * is <code>true</code>.
1441 * Some look and feels might not paint focus state;
1442 * they will ignore this property.
1443 *
1444 * @param b if <code>true</code>, the focus state should be painted
1445 * @see #isFocusPainted
1446 * @beaninfo
1447 * bound: true
1448 * attribute: visualUpdate true
1449 * description: Whether focus should be painted
1450 */
1451 public void setFocusPainted(boolean b) {
1452 boolean oldValue = paintFocus;
1453 paintFocus = b;
1454 firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
1455 if (b != oldValue && isFocusOwner()) {
1456 revalidate();
1457 repaint();
1458 }
1459 }
1460
1461 /**
1462 * Gets the <code>contentAreaFilled</code> property.
1463 *
1464 * @return the <code>contentAreaFilled</code> property
1465 * @see #setContentAreaFilled
1466 */
1467 public boolean isContentAreaFilled() {
1468 return contentAreaFilled;
1469 }
1470
1471 /**
1472 * Sets the <code>contentAreaFilled</code> property.
1473 * If <code>true</code> the button will paint the content
1474 * area. If you wish to have a transparent button, such as
1475 * an icon only button, for example, then you should set
1476 * this to <code>false</code>. Do not call <code>setOpaque(false)</code>.
1477 * The default value for the <code>contentAreaFilled</code>
1478 * property is <code>true</code>.
1479 * <p>
1480 * This function may cause the component's opaque property to change.
1481 * <p>
1482 * The exact behavior of calling this function varies on a
1483 * component-by-component and L&F-by-L&F basis.
1484 *
1485 * @param b if true, the content should be filled; if false
1486 * the content area is not filled
1487 * @see #isContentAreaFilled
1488 * @see #setOpaque
1489 * @beaninfo
1490 * bound: true
1491 * attribute: visualUpdate true
1492 * description: Whether the button should paint the content area
1493 * or leave it transparent.
1494 */
1495 public void setContentAreaFilled(boolean b) {
1496 boolean oldValue = contentAreaFilled;
1497 contentAreaFilled = b;
1498 contentAreaFilledSet = true;
1499 firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
1500 if (b != oldValue) {
1501 repaint();
1502 }
1503 }
1504
1505 /**
1506 * Gets the <code>rolloverEnabled</code> property.
1507 *
1508 * @return the value of the <code>rolloverEnabled</code> property
1509 * @see #setRolloverEnabled
1510 */
1511 public boolean isRolloverEnabled() {
1512 return rolloverEnabled;
1513 }
1514
1515 /**
1516 * Sets the <code>rolloverEnabled</code> property, which
1517 * must be <code>true</code> for rollover effects to occur.
1518 * The default value for the <code>rolloverEnabled</code>
1519 * property is <code>false</code>.
1520 * Some look and feels might not implement rollover effects;
1521 * they will ignore this property.
1522 *
1523 * @param b if <code>true</code>, rollover effects should be painted
1524 * @see #isRolloverEnabled
1525 * @beaninfo
1526 * bound: true
1527 * attribute: visualUpdate true
1528 * description: Whether rollover effects should be enabled.
1529 */
1530 public void setRolloverEnabled(boolean b) {
1531 boolean oldValue = rolloverEnabled;
1532 rolloverEnabled = b;
1533 rolloverEnabledSet = true;
1534 firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
1535 if (b != oldValue) {
1536 repaint();
1537 }
1538 }
1539
1540 /**
1541 * Returns the keyboard mnemonic from the current model.
1542 * @return the keyboard mnemonic from the model
1543 */
1544 public int getMnemonic() {
1545 return mnemonic;
1546 }
1547
1548 /**
1549 * Sets the keyboard mnemonic on the current model.
1550 * The mnemonic is the key which when combined with the look and feel's
1551 * mouseless modifier (usually Alt) will activate this button
1552 * if focus is contained somewhere within this button's ancestor
1553 * window.
1554 * <p>
1555 * A mnemonic must correspond to a single key on the keyboard
1556 * and should be specified using one of the <code>VK_XXX</code>
1557 * keycodes defined in <code>java.awt.event.KeyEvent</code>.
1558 * These codes and the wider array of codes for international
1559 * keyboards may be obtained through
1560 * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>.
1561 * Mnemonics are case-insensitive, therefore a key event
1562 * with the corresponding keycode would cause the button to be
1563 * activated whether or not the Shift modifier was pressed.
1564 * <p>
1565 * If the character defined by the mnemonic is found within
1566 * the button's label string, the first occurrence of it
1567 * will be underlined to indicate the mnemonic to the user.
1568 *
1569 * @param mnemonic the key code which represents the mnemonic
1570 * @see java.awt.event.KeyEvent
1571 * @see #setDisplayedMnemonicIndex
1572 *
1573 * @beaninfo
1574 * bound: true
1575 * attribute: visualUpdate true
1576 * description: the keyboard character mnemonic
1577 */
1578 public void setMnemonic(int mnemonic) {
1579 int oldValue = getMnemonic();
1580 model.setMnemonic(mnemonic);
1581 updateMnemonicProperties();
1582 }
1583
1584 /**
1585 * This method is now obsolete, please use <code>setMnemonic(int)</code>
1586 * to set the mnemonic for a button. This method is only designed
1587 * to handle character values which fall between 'a' and 'z' or
1588 * 'A' and 'Z'.
1589 *
1590 * @param mnemonic a char specifying the mnemonic value
1591 * @see #setMnemonic(int)
1592 * @beaninfo
1593 * bound: true
1594 * attribute: visualUpdate true
1595 * description: the keyboard character mnemonic
1596 */
1597 public void setMnemonic(char mnemonic) {
1598 int vk = (int) mnemonic;
1599 if(vk >= 'a' && vk <='z')
1600 vk -= ('a' - 'A');
1601 setMnemonic(vk);
1602 }
1603
1604 /**
1605 * Provides a hint to the look and feel as to which character in the
1606 * text should be decorated to represent the mnemonic. Not all look and
1607 * feels may support this. A value of -1 indicates either there is no
1608 * mnemonic, the mnemonic character is not contained in the string, or
1609 * the developer does not wish the mnemonic to be displayed.
1610 * <p>
1611 * The value of this is updated as the properties relating to the
1612 * mnemonic change (such as the mnemonic itself, the text...).
1613 * You should only ever have to call this if
1614 * you do not wish the default character to be underlined. For example, if
1615 * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
1616 * to be decorated, as 'Save <u>A</u>s', you would have to invoke
1617 * <code>setDisplayedMnemonicIndex(5)</code> after invoking
1618 * <code>setMnemonic(KeyEvent.VK_A)</code>.
1619 *
1620 * @since 1.4
1621 * @param index Index into the String to underline
1622 * @exception IllegalArgumentException will be thrown if <code>index</code>
1623 * is >= length of the text, or < -1
1624 * @see #getDisplayedMnemonicIndex
1625 *
1626 * @beaninfo
1627 * bound: true
1628 * attribute: visualUpdate true
1629 * description: the index into the String to draw the keyboard character
1630 * mnemonic at
1631 */
1632 public void setDisplayedMnemonicIndex(int index)
1633 throws IllegalArgumentException {
1634 int oldValue = mnemonicIndex;
1635 if (index == -1) {
1636 mnemonicIndex = -1;
1637 } else {
1638 String text = getText();
1639 int textLength = (text == null) ? 0 : text.length();
1640 if (index < -1 || index >= textLength) { // index out of range
1641 throw new IllegalArgumentException("index == " + index);
1642 }
1715 }
1716 this.multiClickThreshhold = threshhold;
1717 }
1718
1719 /**
1720 * Gets the amount of time (in milliseconds) required between
1721 * mouse press events for the button to generate the corresponding
1722 * action events.
1723 *
1724 * @see #setMultiClickThreshhold
1725 * @return the amount of time required between mouse press events
1726 * to generate corresponding action events
1727 * @since 1.4
1728 */
1729 public long getMultiClickThreshhold() {
1730 return multiClickThreshhold;
1731 }
1732
1733 /**
1734 * Returns the model that this button represents.
1735 * @return the <code>model</code> property
1736 * @see #setModel
1737 */
1738 public ButtonModel getModel() {
1739 return model;
1740 }
1741
1742 /**
1743 * Sets the model that this button represents.
1744 * @param newModel the new <code>ButtonModel</code>
1745 * @see #getModel
1746 * @beaninfo
1747 * bound: true
1748 * description: Model that the Button uses.
1749 */
1750 public void setModel(ButtonModel newModel) {
1751
1752 ButtonModel oldModel = getModel();
1753
1754 if (oldModel != null) {
1755 oldModel.removeChangeListener(changeListener);
1756 oldModel.removeActionListener(actionListener);
1757 oldModel.removeItemListener(itemListener);
1758 changeListener = null;
1759 actionListener = null;
1760 itemListener = null;
1761 }
1762
1763 model = newModel;
1764
1785 firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
1786 if (newModel != oldModel) {
1787 revalidate();
1788 repaint();
1789 }
1790 }
1791
1792
1793 /**
1794 * Returns the L&F object that renders this component.
1795 * @return the ButtonUI object
1796 * @see #setUI
1797 */
1798 public ButtonUI getUI() {
1799 return (ButtonUI) ui;
1800 }
1801
1802
1803 /**
1804 * Sets the L&F object that renders this component.
1805 * @param ui the <code>ButtonUI</code> L&F object
1806 * @see #getUI
1807 * @beaninfo
1808 * bound: true
1809 * hidden: true
1810 * attribute: visualUpdate true
1811 * description: The UI object that implements the LookAndFeel.
1812 */
1813 public void setUI(ButtonUI ui) {
1814 super.setUI(ui);
1815 // disabled icons are generated by the LF so they should be unset here
1816 if (disabledIcon instanceof UIResource) {
1817 setDisabledIcon(null);
1818 }
1819 if (disabledSelectedIcon instanceof UIResource) {
1820 setDisabledSelectedIcon(null);
1821 }
1822 }
1823
1824
1825 /**
1826 * Resets the UI property to a value from the current look
1827 * and feel. Subtypes of <code>AbstractButton</code>
1828 * should override this to update the UI. For
1829 * example, <code>JButton</code> might do the following:
1830 * <pre>
1831 * setUI((ButtonUI)UIManager.getUI(
1832 * "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
1833 * </pre>
1834 */
1835 public void updateUI() {
1836 }
1837
1838 /**
1839 * Adds the specified component to this container at the specified
1840 * index, refer to
1841 * {@link java.awt.Container#addImpl(Component, Object, int)}
1842 * for a complete description of this method.
1843 *
1844 * @param comp the component to be added
1845 * @param constraints an object expressing layout constraints
1846 * for this component
1847 * @param index the position in the container's list at which to
1848 * insert the component, where <code>-1</code>
1849 * means append to the end
1850 * @exception IllegalArgumentException if <code>index</code> is invalid
1851 * @exception IllegalArgumentException if adding the container's parent
1852 * to itself
1853 * @exception IllegalArgumentException if adding a window to a container
1854 * @since 1.5
1855 */
1856 protected void addImpl(Component comp, Object constraints, int index) {
1857 if (!setLayout) {
1858 setLayout(new OverlayLayout(this));
1859 }
1860 super.addImpl(comp, constraints, index);
1861 }
1862
1863 /**
1864 * Sets the layout manager for this container, refer to
1865 * {@link java.awt.Container#setLayout(LayoutManager)}
1866 * for a complete description of this method.
1867 *
1868 * @param mgr the specified layout manager
1869 * @since 1.5
1870 */
1871 public void setLayout(LayoutManager mgr) {
1872 setLayout = true;
1873 super.setLayout(mgr);
1874 }
1875
1876 /**
1877 * Adds a <code>ChangeListener</code> to the button.
1878 * @param l the listener to be added
1879 */
1880 public void addChangeListener(ChangeListener l) {
1881 listenerList.add(ChangeListener.class, l);
1882 }
1883
1884 /**
1885 * Removes a ChangeListener from the button.
1886 * @param l the listener to be removed
1887 */
1888 public void removeChangeListener(ChangeListener l) {
1889 listenerList.remove(ChangeListener.class, l);
1890 }
1891
1892 /**
1893 * Returns an array of all the <code>ChangeListener</code>s added
1894 * to this AbstractButton with addChangeListener().
1895 *
1896 * @return all of the <code>ChangeListener</code>s added or an empty
1897 * array if no listeners have been added
1898 * @since 1.4
1899 */
1900 public ChangeListener[] getChangeListeners() {
1901 return listenerList.getListeners(ChangeListener.class);
1902 }
1903
1904 /**
1905 * Notifies all listeners that have registered interest for
1906 * notification on this event type. The event instance
1907 * is lazily created.
1908 * @see EventListenerList
1909 */
1910 protected void fireStateChanged() {
1911 // Guaranteed to return a non-null array
1912 Object[] listeners = listenerList.getListenerList();
1913 // Process the listeners last to first, notifying
1914 // those that are interested in this event
1915 for (int i = listeners.length-2; i>=0; i-=2) {
1916 if (listeners[i]==ChangeListener.class) {
1917 // Lazily create the event:
1918 if (changeEvent == null)
1919 changeEvent = new ChangeEvent(this);
1920 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
1921 }
1922 }
1923 }
1924
1925 /**
1926 * Adds an <code>ActionListener</code> to the button.
1927 * @param l the <code>ActionListener</code> to be added
1928 */
1929 public void addActionListener(ActionListener l) {
1930 listenerList.add(ActionListener.class, l);
1931 }
1932
1933 /**
1934 * Removes an <code>ActionListener</code> from the button.
1935 * If the listener is the currently set <code>Action</code>
1936 * for the button, then the <code>Action</code>
1937 * is set to <code>null</code>.
1938 *
1939 * @param l the listener to be removed
1940 */
1941 public void removeActionListener(ActionListener l) {
1942 if ((l != null) && (getAction() == l)) {
1943 setAction(null);
1944 } else {
1945 listenerList.remove(ActionListener.class, l);
1946 }
1947 }
1948
1949 /**
1950 * Returns an array of all the <code>ActionListener</code>s added
1951 * to this AbstractButton with addActionListener().
1952 *
1953 * @return all of the <code>ActionListener</code>s added or an empty
1954 * array if no listeners have been added
1955 * @since 1.4
1956 */
1957 public ActionListener[] getActionListeners() {
1958 return listenerList.getListeners(ActionListener.class);
1959 }
1960
1961 /**
1962 * Subclasses that want to handle <code>ChangeEvents</code> differently
1963 * can override this to return another <code>ChangeListener</code>
1964 * implementation.
1965 *
1966 * @return the new <code>ChangeListener</code>
1967 */
1968 protected ChangeListener createChangeListener() {
1969 return getHandler();
1970 }
1971
1972 /**
1973 * Extends <code>ChangeListener</code> to be serializable.
1974 * <p>
1975 * <strong>Warning:</strong>
1976 * Serialized objects of this class will not be compatible with
1977 * future Swing releases. The current serialization support is
1978 * appropriate for short term storage or RMI between applications running
1979 * the same version of Swing. As of 1.4, support for long term storage
1980 * of all JavaBeans™
1981 * has been added to the <code>java.beans</code> package.
1982 * Please see {@link java.beans.XMLEncoder}.
1983 */
1984 @SuppressWarnings("serial")
1985 protected class ButtonChangeListener implements ChangeListener, Serializable {
1986 // NOTE: This class is NOT used, instead the functionality has
1987 // been moved to Handler.
1988 ButtonChangeListener() {
1989 }
1990
1991 public void stateChanged(ChangeEvent e) {
1992 getHandler().stateChanged(e);
1993 }
1994 }
1995
1996
1997 /**
1998 * Notifies all listeners that have registered interest for
1999 * notification on this event type. The event instance
2000 * is lazily created using the <code>event</code>
2001 * parameter.
2002 *
2003 * @param event the <code>ActionEvent</code> object
2004 * @see EventListenerList
2005 */
2006 protected void fireActionPerformed(ActionEvent event) {
2007 // Guaranteed to return a non-null array
2008 Object[] listeners = listenerList.getListenerList();
2009 ActionEvent e = null;
2010 // Process the listeners last to first, notifying
2011 // those that are interested in this event
2012 for (int i = listeners.length-2; i>=0; i-=2) {
2013 if (listeners[i]==ActionListener.class) {
2014 // Lazily create the event:
2015 if (e == null) {
2016 String actionCommand = event.getActionCommand();
2017 if(actionCommand == null) {
2018 actionCommand = getActionCommand();
2019 }
2020 e = new ActionEvent(AbstractButton.this,
2021 ActionEvent.ACTION_PERFORMED,
2022 actionCommand,
2023 event.getWhen(),
2024 event.getModifiers());
2025 }
2026 ((ActionListener)listeners[i+1]).actionPerformed(e);
2027 }
2028 }
2029 }
2030
2031 /**
2032 * Notifies all listeners that have registered interest for
2033 * notification on this event type. The event instance
2034 * is lazily created using the <code>event</code> parameter.
2035 *
2036 * @param event the <code>ItemEvent</code> object
2037 * @see EventListenerList
2038 */
2039 protected void fireItemStateChanged(ItemEvent event) {
2040 // Guaranteed to return a non-null array
2041 Object[] listeners = listenerList.getListenerList();
2042 ItemEvent e = null;
2043 // Process the listeners last to first, notifying
2044 // those that are interested in this event
2045 for (int i = listeners.length-2; i>=0; i-=2) {
2046 if (listeners[i]==ItemListener.class) {
2047 // Lazily create the event:
2048 if (e == null) {
2049 e = new ItemEvent(AbstractButton.this,
2050 ItemEvent.ITEM_STATE_CHANGED,
2051 AbstractButton.this,
2052 event.getStateChange());
2053 }
2054 ((ItemListener)listeners[i+1]).itemStateChanged(e);
2055 }
2056 }
2092 }
2093
2094
2095 /**
2096 * Enables (or disables) the button.
2097 * @param b true to enable the button, otherwise false
2098 */
2099 public void setEnabled(boolean b) {
2100 if (!b && model.isRollover()) {
2101 model.setRollover(false);
2102 }
2103 super.setEnabled(b);
2104 model.setEnabled(b);
2105 }
2106
2107 // *** Deprecated java.awt.Button APIs below *** //
2108
2109 /**
2110 * Returns the label text.
2111 *
2112 * @return a <code>String</code> containing the label
2113 * @deprecated - Replaced by <code>getText</code>
2114 */
2115 @Deprecated
2116 public String getLabel() {
2117 return getText();
2118 }
2119
2120 /**
2121 * Sets the label text.
2122 *
2123 * @param label a <code>String</code> containing the text
2124 * @deprecated - Replaced by <code>setText(text)</code>
2125 * @beaninfo
2126 * bound: true
2127 * description: Replace by setText(text)
2128 */
2129 @Deprecated
2130 public void setLabel(String label) {
2131 setText(label);
2132 }
2133
2134 /**
2135 * Adds an <code>ItemListener</code> to the <code>checkbox</code>.
2136 * @param l the <code>ItemListener</code> to be added
2137 */
2138 public void addItemListener(ItemListener l) {
2139 listenerList.add(ItemListener.class, l);
2140 }
2141
2142 /**
2143 * Removes an <code>ItemListener</code> from the button.
2144 * @param l the <code>ItemListener</code> to be removed
2145 */
2146 public void removeItemListener(ItemListener l) {
2147 listenerList.remove(ItemListener.class, l);
2148 }
2149
2150 /**
2151 * Returns an array of all the <code>ItemListener</code>s added
2152 * to this AbstractButton with addItemListener().
2153 *
2154 * @return all of the <code>ItemListener</code>s added or an empty
2155 * array if no listeners have been added
2156 * @since 1.4
2157 */
2158 public ItemListener[] getItemListeners() {
2159 return listenerList.getListeners(ItemListener.class);
2160 }
2161
2162 /**
2163 * Returns an array (length 1) containing the label or
2164 * <code>null</code> if the button is not selected.
2165 *
2166 * @return an array containing 1 Object: the text of the button,
2167 * if the item is selected; otherwise <code>null</code>
2168 */
2169 public Object[] getSelectedObjects() {
2170 if (isSelected() == false) {
2171 return null;
2172 }
2173 Object[] selectedObjects = new Object[1];
2174 selectedObjects[0] = getText();
2175 return selectedObjects;
2176 }
2177
2178 /**
2179 * Initialization of the {@code AbstractButton}.
2180 *
2181 * @param text the text of the button
2182 * @param icon the Icon image to display on the button
2183 */
2184 protected void init(String text, Icon icon) {
2185 if(text != null) {
2186 setText(text);
2187 }
2188
2189 if(icon != null) {
2190 setIcon(icon);
2191 }
2192
2193 // Set the UI
2194 updateUI();
2195
2196 setAlignmentX(LEFT_ALIGNMENT);
2197 setAlignmentY(CENTER_ALIGNMENT);
2198 }
2199
2200
2201 /**
2202 * This is overridden to return false if the current <code>Icon</code>'s
2203 * <code>Image</code> is not equal to the
2204 * passed in <code>Image</code> <code>img</code>.
2205 *
2206 * @param img the <code>Image</code> to be compared
2207 * @param infoflags flags used to repaint the button when the image
2208 * is updated and which determine how much is to be painted
2209 * @param x the x coordinate
2210 * @param y the y coordinate
2211 * @param w the width
2212 * @param h the height
2213 * @see java.awt.image.ImageObserver
2214 * @see java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
2215 */
2216 public boolean imageUpdate(Image img, int infoflags,
2217 int x, int y, int w, int h) {
2218 Icon iconDisplayed = null;
2219
2220 if (!model.isEnabled()) {
2221 if (model.isSelected()) {
2222 iconDisplayed = getDisabledSelectedIcon();
2223 } else {
2224 iconDisplayed = getDisabledIcon();
2225 }
2226 } else if (model.isPressed() && model.isArmed()) {
2258 if (!rolloverEnabledSet) {
2259 setRolloverEnabled(((Boolean)value).booleanValue());
2260 rolloverEnabledSet = false;
2261 }
2262 } else if (propertyName == "iconTextGap") {
2263 if (!iconTextGapSet) {
2264 setIconTextGap(((Number)value).intValue());
2265 iconTextGapSet = false;
2266 }
2267 } else if (propertyName == "contentAreaFilled") {
2268 if (!contentAreaFilledSet) {
2269 setContentAreaFilled(((Boolean)value).booleanValue());
2270 contentAreaFilledSet = false;
2271 }
2272 } else {
2273 super.setUIProperty(propertyName, value);
2274 }
2275 }
2276
2277 /**
2278 * Returns a string representation of this <code>AbstractButton</code>.
2279 * This method
2280 * is intended to be used only for debugging purposes, and the
2281 * content and format of the returned string may vary between
2282 * implementations. The returned string may be empty but may not
2283 * be <code>null</code>.
2284 * <P>
2285 * Overriding <code>paramString</code> to provide information about the
2286 * specific new aspects of the JFC components.
2287 *
2288 * @return a string representation of this <code>AbstractButton</code>
2289 */
2290 protected String paramString() {
2291 String defaultIconString = ((defaultIcon != null)
2292 && (defaultIcon != this) ?
2293 defaultIcon.toString() : "");
2294 String pressedIconString = ((pressedIcon != null)
2295 && (pressedIcon != this) ?
2296 pressedIcon.toString() : "");
2297 String disabledIconString = ((disabledIcon != null)
2298 && (disabledIcon != this) ?
2299 disabledIcon.toString() : "");
2300 String selectedIconString = ((selectedIcon != null)
2301 && (selectedIcon != this) ?
2302 selectedIcon.toString() : "");
2303 String disabledSelectedIconString = ((disabledSelectedIcon != null) &&
2304 (disabledSelectedIcon != this) ?
2305 disabledSelectedIcon.toString()
2306 : "");
2307 String rolloverIconString = ((rolloverIcon != null)
2308 && (rolloverIcon != this) ?
2373 fireItemStateChanged(event);
2374 if (shouldUpdateSelectedStateFromAction()) {
2375 Action action = getAction();
2376 if (action != null && AbstractAction.hasSelectedKey(action)) {
2377 boolean selected = isSelected();
2378 boolean isActionSelected = AbstractAction.isSelected(
2379 action);
2380 if (isActionSelected != selected) {
2381 action.putValue(Action.SELECTED_KEY, selected);
2382 }
2383 }
2384 }
2385 }
2386 }
2387
2388 ///////////////////
2389 // Accessibility support
2390 ///////////////////
2391 /**
2392 * This class implements accessibility support for the
2393 * <code>AbstractButton</code> class. It provides an implementation of the
2394 * Java Accessibility API appropriate to button and menu item
2395 * user-interface elements.
2396 * <p>
2397 * <strong>Warning:</strong>
2398 * Serialized objects of this class will not be compatible with
2399 * future Swing releases. The current serialization support is
2400 * appropriate for short term storage or RMI between applications running
2401 * the same version of Swing. As of 1.4, support for long term storage
2402 * of all JavaBeans™
2403 * has been added to the <code>java.beans</code> package.
2404 * Please see {@link java.beans.XMLEncoder}.
2405 * @since 1.4
2406 */
2407 @SuppressWarnings("serial") // Same-version serialization only
2408 protected abstract class AccessibleAbstractButton
2409 extends AccessibleJComponent implements AccessibleAction,
2410 AccessibleValue, AccessibleText, AccessibleExtendedComponent {
2411
2412 /**
2413 * Returns the accessible name of this object.
2414 *
2415 * @return the localized name of the object -- can be
2416 * <code>null</code> if this
2417 * object does not have a name
2418 */
2419 public String getAccessibleName() {
2420 String name = accessibleName;
2421
2422 if (name == null) {
2423 name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
2424 }
2425 if (name == null) {
2426 name = AbstractButton.this.getText();
2427 }
2428 if (name == null) {
2429 name = super.getAccessibleName();
2430 }
2431 return name;
2432 }
2433
2434 /**
2435 * Get the AccessibleIcons associated with this object if one
2436 * or more exist. Otherwise return null.
3080
3081 ButtonKeyBinding(int mnemonic) {
3082 this.mnemonic = mnemonic;
3083 }
3084
3085 /**
3086 * Returns the number of key bindings for this object
3087 *
3088 * @return the zero-based number of key bindings for this object
3089 */
3090 public int getAccessibleKeyBindingCount() {
3091 return 1;
3092 }
3093
3094 /**
3095 * Returns a key binding for this object. The value returned is an
3096 * java.lang.Object which must be cast to appropriate type depending
3097 * on the underlying implementation of the key. For example, if the
3098 * Object returned is a javax.swing.KeyStroke, the user of this
3099 * method should do the following:
3100 * <nf><code>
3101 * Component c = <get the component that has the key bindings>
3102 * AccessibleContext ac = c.getAccessibleContext();
3103 * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
3104 * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
3105 * Object o = akb.getAccessibleKeyBinding(i);
3106 * if (o instanceof javax.swing.KeyStroke) {
3107 * javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
3108 * <do something with the key binding>
3109 * }
3110 * }
3111 * </code></nf>
3112 *
3113 * @param i zero-based index of the key bindings
3114 * @return a javax.lang.Object which specifies the key binding
3115 * @exception IllegalArgumentException if the index is
3116 * out of bounds
3117 * @see #getAccessibleKeyBindingCount
3118 */
3119 public java.lang.Object getAccessibleKeyBinding(int i) {
3120 if (i != 0) {
3121 throw new IllegalArgumentException();
3122 }
3123 return KeyStroke.getKeyStroke(mnemonic, 0);
3124 }
3125 }
3126 }
3127 }
|
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} 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}</a> for more
55 * details, and you can find more information in <a
56 * href="http://docs.oracle.com/javase/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://docs.oracle.com/javase/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™
70 * has been added to the {@code java.beans} package.
71 * Please see {@link java.beans.XMLEncoder}.
72 *
73 * @author Jeff Dinkins
74 * @since 1.2
75 */
76 @SuppressWarnings("serial") // Same-version serialization only
77 public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {
78
79 // *********************************
80 // ******* Button properties *******
81 // *********************************
82
83 /** Identifies a change in the button model. */
84 public static final String MODEL_CHANGED_PROPERTY = "model";
85 /** Identifies a change in the button's text. */
86 public static final String TEXT_CHANGED_PROPERTY = "text";
87 /** Identifies a change to the button's mnemonic. */
88 public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
89
90 // Text positioning and alignment
199 private long multiClickThreshhold = 0;
200
201 private boolean borderPaintedSet = false;
202 private boolean rolloverEnabledSet = false;
203 private boolean iconTextGapSet = false;
204 private boolean contentAreaFilledSet = false;
205
206 // Whether or not we've set the LayoutManager.
207 private boolean setLayout = false;
208
209 // This is only used by JButton, promoted to avoid an extra
210 // boolean field in JButton
211 boolean defaultCapable = true;
212
213 /**
214 * Combined listeners: ActionListener, ChangeListener, ItemListener.
215 */
216 private Handler handler;
217
218 /**
219 * The button model's {@code changeListener}.
220 */
221 protected ChangeListener changeListener = null;
222 /**
223 * The button model's {@code ActionListener}.
224 */
225 protected ActionListener actionListener = null;
226 /**
227 * The button model's {@code ItemListener}.
228 */
229 protected ItemListener itemListener = null;
230
231 /**
232 * Only one {@code ChangeEvent} is needed per button
233 * instance since the
234 * event's only state is the source property. The source of events
235 * generated is always "this".
236 */
237 protected transient ChangeEvent changeEvent;
238
239 private boolean hideActionText = false;
240
241 /**
242 * Sets the {@code hideActionText} property, which determines
243 * whether the button displays text from the {@code Action}.
244 * This is useful only if an {@code Action} has been
245 * installed on the button.
246 *
247 * @param hideActionText {@code true} if the button's
248 * {@code text} property should not reflect
249 * that of the {@code Action}; the default is
250 * {@code false}
251 * @see <a href="Action.html#buttonActions">Swing Components Supporting
252 * <code>Action</code></a>
253 * @since 1.6
254 * @beaninfo
255 * bound: true
256 * expert: true
257 * description: Whether the text of the button should come from
258 * the {@code Action}.
259 */
260 public void setHideActionText(boolean hideActionText) {
261 if (hideActionText != this.hideActionText) {
262 this.hideActionText = hideActionText;
263 if (getAction() != null) {
264 setTextFromAction(getAction(), false);
265 }
266 firePropertyChange("hideActionText", !hideActionText,
267 hideActionText);
268 }
269 }
270
271 /**
272 * Returns the value of the {@code hideActionText} property, which
273 * determines whether the button displays text from the
274 * {@code Action}. This is useful only if an {@code Action}
275 * has been installed on the button.
276 *
277 * @return {@code true} if the button's {@code text}
278 * property should not reflect that of the
279 * {@code Action}; the default is {@code false}
280 * @since 1.6
281 */
282 public boolean getHideActionText() {
283 return hideActionText;
284 }
285
286 /**
287 * Returns the button's text.
288 * @return the buttons text
289 * @see #setText
290 */
291 public String getText() {
292 return text;
293 }
294
295 /**
296 * Sets the button's text.
297 * @param text the string used to set the text
298 * @see #getText
299 * @beaninfo
314 oldValue, text);
315 }
316 if (text == null || oldValue == null || !text.equals(oldValue)) {
317 revalidate();
318 repaint();
319 }
320 }
321
322
323 /**
324 * Returns the state of the button. True if the
325 * toggle button is selected, false if it's not.
326 * @return true if the toggle button is selected, otherwise false
327 */
328 public boolean isSelected() {
329 return model.isSelected();
330 }
331
332 /**
333 * Sets the state of the button. Note that this method does not
334 * trigger an {@code actionEvent}.
335 * Call {@code doClick} to perform a programmatic action change.
336 *
337 * @param b true if the button is selected, otherwise false
338 */
339 public void setSelected(boolean b) {
340 boolean oldValue = isSelected();
341
342 // TIGER - 4840653
343 // Removed code which fired an AccessibleState.SELECTED
344 // PropertyChangeEvent since this resulted in two
345 // identical events being fired since
346 // AbstractButton.fireItemStateChanged also fires the
347 // same event. This caused screen readers to speak the
348 // name of the item twice.
349
350 model.setSelected(b);
351 }
352
353 /**
354 * Programmatically perform a "click". This does the same
355 * thing as if the user had pressed and released the button.
356 */
357 public void doClick() {
358 doClick(68);
359 }
360
361 /**
362 * Programmatically perform a "click". This does the same
363 * thing as if the user had pressed and released the button.
364 * The button stays visually "pressed" for {@code pressTime}
365 * milliseconds.
366 *
367 * @param pressTime the time to "hold down" the button, in milliseconds
368 */
369 public void doClick(int pressTime) {
370 Dimension size = getSize();
371 model.setArmed(true);
372 model.setPressed(true);
373 paintImmediately(new Rectangle(0,0, size.width, size.height));
374 try {
375 Thread.sleep(pressTime);
376 } catch(InterruptedException ie) {
377 }
378 model.setPressed(false);
379 model.setArmed(false);
380 }
381
382 /**
383 * Sets space for margin between the button's border and
384 * the label. Setting to {@code null} will cause the button to
385 * use the default margin. The button's default {@code Border}
386 * object will use this value to create the proper margin.
387 * However, if a non-default border is set on the button,
388 * it is that {@code Border} object's responsibility to create the
389 * appropriate margin space (else this property will
390 * effectively be ignored).
391 *
392 * @param m the space between the border and the label
393 *
394 * @beaninfo
395 * bound: true
396 * attribute: visualUpdate true
397 * description: The space between the button's border and the label.
398 */
399 public void setMargin(Insets m) {
400 // Cache the old margin if it comes from the UI
401 if(m instanceof UIResource) {
402 defaultMargin = m;
403 } else if(margin instanceof UIResource) {
404 defaultMargin = margin;
405 }
406
407 // If the client passes in a null insets, restore the margin
408 // from the UI if possible
409 if(m == null && defaultMargin != null) {
410 m = defaultMargin;
411 }
412
413 Insets old = margin;
414 margin = m;
415 firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
416 if (old == null || !old.equals(m)) {
417 revalidate();
418 repaint();
419 }
420 }
421
422 /**
423 * Returns the margin between the button's border and
424 * the label.
425 *
426 * @return an {@code Insets} object specifying the margin
427 * between the botton's border and the label
428 * @see #setMargin
429 */
430 public Insets getMargin() {
431 return (margin == null) ? null : (Insets) margin.clone();
432 }
433
434 /**
435 * Returns the default icon.
436 * @return the default {@code Icon}
437 * @see #setIcon
438 */
439 public Icon getIcon() {
440 return defaultIcon;
441 }
442
443 /**
444 * Sets the button's default icon. This icon is
445 * also used as the "pressed" and "disabled" icon if
446 * there is no explicitly set pressed icon.
447 *
448 * @param defaultIcon the icon used as the default image
449 * @see #getIcon
450 * @see #setPressedIcon
451 * @beaninfo
452 * bound: true
453 * attribute: visualUpdate true
454 * description: The button's default icon
455 */
456 public void setIcon(Icon defaultIcon) {
467 }
468
469 firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
470 if (accessibleContext != null) {
471 accessibleContext.firePropertyChange(
472 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
473 oldValue, defaultIcon);
474 }
475 if (defaultIcon != oldValue) {
476 if (defaultIcon == null || oldValue == null ||
477 defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
478 defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
479 revalidate();
480 }
481 repaint();
482 }
483 }
484
485 /**
486 * Returns the pressed icon for the button.
487 * @return the {@code pressedIcon} property
488 * @see #setPressedIcon
489 */
490 public Icon getPressedIcon() {
491 return pressedIcon;
492 }
493
494 /**
495 * Sets the pressed icon for the button.
496 * @param pressedIcon the icon used as the "pressed" image
497 * @see #getPressedIcon
498 * @beaninfo
499 * bound: true
500 * attribute: visualUpdate true
501 * description: The pressed icon for the button.
502 */
503 public void setPressedIcon(Icon pressedIcon) {
504 Icon oldValue = this.pressedIcon;
505 this.pressedIcon = pressedIcon;
506 firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
507 if (accessibleContext != null) {
508 accessibleContext.firePropertyChange(
509 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
510 oldValue, pressedIcon);
511 }
512 if (pressedIcon != oldValue) {
513 if (getModel().isPressed()) {
514 repaint();
515 }
516 }
517 }
518
519 /**
520 * Returns the selected icon for the button.
521 * @return the {@code selectedIcon} property
522 * @see #setSelectedIcon
523 */
524 public Icon getSelectedIcon() {
525 return selectedIcon;
526 }
527
528 /**
529 * Sets the selected icon for the button.
530 * @param selectedIcon the icon used as the "selected" image
531 * @see #getSelectedIcon
532 * @beaninfo
533 * bound: true
534 * attribute: visualUpdate true
535 * description: The selected icon for the button.
536 */
537 public void setSelectedIcon(Icon selectedIcon) {
538 Icon oldValue = this.selectedIcon;
539 this.selectedIcon = selectedIcon;
540
541 /* If the default selected icon has really changed and we had
547 disabledSelectedIcon instanceof UIResource) {
548
549 disabledSelectedIcon = null;
550 }
551
552 firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
553 if (accessibleContext != null) {
554 accessibleContext.firePropertyChange(
555 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
556 oldValue, selectedIcon);
557 }
558 if (selectedIcon != oldValue) {
559 if (isSelected()) {
560 repaint();
561 }
562 }
563 }
564
565 /**
566 * Returns the rollover icon for the button.
567 * @return the {@code rolloverIcon} property
568 * @see #setRolloverIcon
569 */
570 public Icon getRolloverIcon() {
571 return rolloverIcon;
572 }
573
574 /**
575 * Sets the rollover icon for the button.
576 * @param rolloverIcon the icon used as the "rollover" image
577 * @see #getRolloverIcon
578 * @beaninfo
579 * bound: true
580 * attribute: visualUpdate true
581 * description: The rollover icon for the button.
582 */
583 public void setRolloverIcon(Icon rolloverIcon) {
584 Icon oldValue = this.rolloverIcon;
585 this.rolloverIcon = rolloverIcon;
586 firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
587 if (accessibleContext != null) {
588 accessibleContext.firePropertyChange(
589 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
590 oldValue, rolloverIcon);
591 }
592 setRolloverEnabled(true);
593 if (rolloverIcon != oldValue) {
594 // No way to determine whether we are currently in
595 // a rollover state, so repaint regardless
596 repaint();
597 }
598
599 }
600
601 /**
602 * Returns the rollover selection icon for the button.
603 * @return the {@code rolloverSelectedIcon} property
604 * @see #setRolloverSelectedIcon
605 */
606 public Icon getRolloverSelectedIcon() {
607 return rolloverSelectedIcon;
608 }
609
610 /**
611 * Sets the rollover selected icon for the button.
612 * @param rolloverSelectedIcon the icon used as the
613 * "selected rollover" image
614 * @see #getRolloverSelectedIcon
615 * @beaninfo
616 * bound: true
617 * attribute: visualUpdate true
618 * description: The rollover selected icon for the button.
619 */
620 public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
621 Icon oldValue = this.rolloverSelectedIcon;
622 this.rolloverSelectedIcon = rolloverSelectedIcon;
623 firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
627 oldValue, rolloverSelectedIcon);
628 }
629 setRolloverEnabled(true);
630 if (rolloverSelectedIcon != oldValue) {
631 // No way to determine whether we are currently in
632 // a rollover state, so repaint regardless
633 if (isSelected()) {
634 repaint();
635 }
636 }
637 }
638
639 /**
640 * Returns the icon used by the button when it's disabled.
641 * If no disabled icon has been set this will forward the call to
642 * the look and feel to construct an appropriate disabled Icon.
643 * <p>
644 * Some look and feels might not render the disabled Icon, in which
645 * case they will ignore this.
646 *
647 * @return the {@code disabledIcon} property
648 * @see #getPressedIcon
649 * @see #setDisabledIcon
650 * @see javax.swing.LookAndFeel#getDisabledIcon
651 */
652 @Transient
653 public Icon getDisabledIcon() {
654 if (disabledIcon == null) {
655 disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
656 if (disabledIcon != null) {
657 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);
658 }
659 }
660 return disabledIcon;
661 }
662
663 /**
664 * Sets the disabled icon for the button.
665 * @param disabledIcon the icon used as the disabled image
666 * @see #getDisabledIcon
667 * @beaninfo
673 Icon oldValue = this.disabledIcon;
674 this.disabledIcon = disabledIcon;
675 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
676 if (accessibleContext != null) {
677 accessibleContext.firePropertyChange(
678 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
679 oldValue, disabledIcon);
680 }
681 if (disabledIcon != oldValue) {
682 if (!isEnabled()) {
683 repaint();
684 }
685 }
686 }
687
688 /**
689 * Returns the icon used by the button when it's disabled and selected.
690 * If no disabled selection icon has been set, this will forward
691 * the call to the LookAndFeel to construct an appropriate disabled
692 * Icon from the selection icon if it has been set and to
693 * {@code getDisabledIcon()} otherwise.
694 * <p>
695 * Some look and feels might not render the disabled selected Icon, in
696 * which case they will ignore this.
697 *
698 * @return the {@code disabledSelectedIcon} property
699 * @see #getDisabledIcon
700 * @see #setDisabledSelectedIcon
701 * @see javax.swing.LookAndFeel#getDisabledSelectedIcon
702 */
703 public Icon getDisabledSelectedIcon() {
704 if (disabledSelectedIcon == null) {
705 if (selectedIcon != null) {
706 disabledSelectedIcon = UIManager.getLookAndFeel().
707 getDisabledSelectedIcon(this, getSelectedIcon());
708 } else {
709 return getDisabledIcon();
710 }
711 }
712 return disabledSelectedIcon;
713 }
714
715 /**
716 * Sets the disabled selection icon for the button.
717 * @param disabledSelectedIcon the icon used as the disabled
718 * selection image
729 if (accessibleContext != null) {
730 accessibleContext.firePropertyChange(
731 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
732 oldValue, disabledSelectedIcon);
733 }
734 if (disabledSelectedIcon != oldValue) {
735 if (disabledSelectedIcon == null || oldValue == null ||
736 disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
737 disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
738 revalidate();
739 }
740 if (!isEnabled() && isSelected()) {
741 repaint();
742 }
743 }
744 }
745
746 /**
747 * Returns the vertical alignment of the text and icon.
748 *
749 * @return the {@code verticalAlignment} property, one of the
750 * following values:
751 * <ul>
752 * <li>{@code SwingConstants.CENTER} (the default)
753 * <li>{@code SwingConstants.TOP}
754 * <li>{@code SwingConstants.BOTTOM}
755 * </ul>
756 */
757 public int getVerticalAlignment() {
758 return verticalAlignment;
759 }
760
761 /**
762 * Sets the vertical alignment of the icon and text.
763 * @param alignment one of the following values:
764 * <ul>
765 * <li>{@code SwingConstants.CENTER} (the default)
766 * <li>{@code SwingConstants.TOP}
767 * <li>{@code SwingConstants.BOTTOM}
768 * </ul>
769 * @throws IllegalArgumentException if the alignment is not one of the legal
771 * @beaninfo
772 * bound: true
773 * enum: TOP SwingConstants.TOP
774 * CENTER SwingConstants.CENTER
775 * BOTTOM SwingConstants.BOTTOM
776 * attribute: visualUpdate true
777 * description: The vertical alignment of the icon and text.
778 */
779 public void setVerticalAlignment(int alignment) {
780 if (alignment == verticalAlignment) return;
781 int oldValue = verticalAlignment;
782 verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
783 firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment); repaint();
784 }
785
786 /**
787 * Returns the horizontal alignment of the icon and text.
788 * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
789 * but subclasses such as {@code JCheckBox} may use a different default.
790 *
791 * @return the {@code horizontalAlignment} property,
792 * one of the following values:
793 * <ul>
794 * <li>{@code SwingConstants.RIGHT}
795 * <li>{@code SwingConstants.LEFT}
796 * <li>{@code SwingConstants.CENTER}
797 * <li>{@code SwingConstants.LEADING}
798 * <li>{@code SwingConstants.TRAILING}
799 * </ul>
800 */
801 public int getHorizontalAlignment() {
802 return horizontalAlignment;
803 }
804
805 /**
806 * Sets the horizontal alignment of the icon and text.
807 * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
808 * but subclasses such as {@code JCheckBox} may use a different default.
809 *
810 * @param alignment the alignment value, one of the following values:
811 * <ul>
823 * CENTER SwingConstants.CENTER
824 * RIGHT SwingConstants.RIGHT
825 * LEADING SwingConstants.LEADING
826 * TRAILING SwingConstants.TRAILING
827 * attribute: visualUpdate true
828 * description: The horizontal alignment of the icon and text.
829 */
830 public void setHorizontalAlignment(int alignment) {
831 if (alignment == horizontalAlignment) return;
832 int oldValue = horizontalAlignment;
833 horizontalAlignment = checkHorizontalKey(alignment,
834 "horizontalAlignment");
835 firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
836 oldValue, horizontalAlignment);
837 repaint();
838 }
839
840
841 /**
842 * Returns the vertical position of the text relative to the icon.
843 * @return the {@code verticalTextPosition} property,
844 * one of the following values:
845 * <ul>
846 * <li>{@code SwingConstants.CENTER} (the default)
847 * <li>{@code SwingConstants.TOP}
848 * <li>{@code SwingConstants.BOTTOM}
849 * </ul>
850 */
851 public int getVerticalTextPosition() {
852 return verticalTextPosition;
853 }
854
855 /**
856 * Sets the vertical position of the text relative to the icon.
857 * @param textPosition one of the following values:
858 * <ul>
859 * <li>{@code SwingConstants.CENTER} (the default)
860 * <li>{@code SwingConstants.TOP}
861 * <li>{@code SwingConstants.BOTTOM}
862 * </ul>
863 * @beaninfo
864 * bound: true
865 * enum: TOP SwingConstants.TOP
866 * CENTER SwingConstants.CENTER
867 * BOTTOM SwingConstants.BOTTOM
868 * attribute: visualUpdate true
869 * description: The vertical position of the text relative to the icon.
870 */
871 public void setVerticalTextPosition(int textPosition) {
872 if (textPosition == verticalTextPosition) return;
873 int oldValue = verticalTextPosition;
874 verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
875 firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
876 revalidate();
877 repaint();
878 }
879
880 /**
881 * Returns the horizontal position of the text relative to the icon.
882 * @return the {@code horizontalTextPosition} property,
883 * one of the following values:
884 * <ul>
885 * <li>{@code SwingConstants.RIGHT}
886 * <li>{@code SwingConstants.LEFT}
887 * <li>{@code SwingConstants.CENTER}
888 * <li>{@code SwingConstants.LEADING}
889 * <li>{@code SwingConstants.TRAILING} (the default)
890 * </ul>
891 */
892 public int getHorizontalTextPosition() {
893 return horizontalTextPosition;
894 }
895
896 /**
897 * Sets the horizontal position of the text relative to the icon.
898 * @param textPosition one of the following values:
899 * <ul>
900 * <li>{@code SwingConstants.RIGHT}
901 * <li>{@code SwingConstants.LEFT}
902 * <li>{@code SwingConstants.CENTER}
903 * <li>{@code SwingConstants.LEADING}
904 * <li>{@code SwingConstants.TRAILING} (the default)
905 * </ul>
906 * @exception IllegalArgumentException if {@code textPosition}
907 * is not one of the legal values listed above
908 * @beaninfo
909 * bound: true
910 * enum: LEFT SwingConstants.LEFT
911 * CENTER SwingConstants.CENTER
912 * RIGHT SwingConstants.RIGHT
913 * LEADING SwingConstants.LEADING
914 * TRAILING SwingConstants.TRAILING
915 * attribute: visualUpdate true
916 * description: The horizontal position of the text relative to the icon.
917 */
918 public void setHorizontalTextPosition(int textPosition) {
919 if (textPosition == horizontalTextPosition) return;
920 int oldValue = horizontalTextPosition;
921 horizontalTextPosition = checkHorizontalKey(textPosition,
922 "horizontalTextPosition");
923 firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
924 oldValue,
925 horizontalTextPosition);
926 revalidate();
1046 public void setActionCommand(String actionCommand) {
1047 getModel().setActionCommand(actionCommand);
1048 }
1049
1050 /**
1051 * Returns the action command for this button.
1052 * @return the action command for this button
1053 */
1054 public String getActionCommand() {
1055 String ac = getModel().getActionCommand();
1056 if(ac == null) {
1057 ac = getText();
1058 }
1059 return ac;
1060 }
1061
1062 private Action action;
1063 private PropertyChangeListener actionPropertyChangeListener;
1064
1065 /**
1066 * Sets the {@code Action}.
1067 * The new {@code Action} replaces any previously set
1068 * {@code Action} but does not affect {@code ActionListeners}
1069 * independently added with {@code addActionListener}.
1070 * If the {@code Action} is already a registered
1071 * {@code ActionListener} for the button, it is not re-registered.
1072 * <p>
1073 * Setting the {@code Action} results in immediately changing
1074 * all the properties described in <a href="Action.html#buttonActions">
1075 * Swing Components Supporting {@code Action}</a>.
1076 * Subsequently, the button's properties are automatically updated
1077 * as the {@code Action}'s properties change.
1078 * <p>
1079 * This method uses three other methods to set
1080 * and help track the {@code Action}'s property values.
1081 * It uses the {@code configurePropertiesFromAction} method
1082 * to immediately change the button's properties.
1083 * To track changes in the {@code Action}'s property values,
1084 * this method registers the {@code PropertyChangeListener}
1085 * returned by {@code createActionPropertyChangeListener}. The
1086 * default {@code PropertyChangeListener} invokes the
1087 * {@code actionPropertyChanged} method when a property in the
1088 * {@code Action} changes.
1089 *
1090 * @param a the {@code Action} for the {@code AbstractButton},
1091 * or {@code null}
1092 * @since 1.3
1093 * @see Action
1094 * @see #getAction
1095 * @see #configurePropertiesFromAction
1096 * @see #createActionPropertyChangeListener
1097 * @see #actionPropertyChanged
1098 * @beaninfo
1099 * bound: true
1100 * attribute: visualUpdate true
1101 * description: the Action instance connected with this ActionEvent source
1102 */
1103 public void setAction(Action a) {
1104 Action oldValue = getAction();
1105 if (action==null || !action.equals(a)) {
1106 action = a;
1107 if (oldValue!=null) {
1108 removeActionListener(oldValue);
1109 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1110 actionPropertyChangeListener = null;
1111 }
1118 // Reverse linkage:
1119 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1120 action.addPropertyChangeListener(actionPropertyChangeListener);
1121 }
1122 firePropertyChange("action", oldValue, action);
1123 }
1124 }
1125
1126 private boolean isListener(Class<?> c, ActionListener a) {
1127 boolean isListener = false;
1128 Object[] listeners = listenerList.getListenerList();
1129 for (int i = listeners.length-2; i>=0; i-=2) {
1130 if (listeners[i]==c && listeners[i+1]==a) {
1131 isListener=true;
1132 }
1133 }
1134 return isListener;
1135 }
1136
1137 /**
1138 * Returns the currently set {@code Action} for this
1139 * {@code ActionEvent} source, or {@code null}
1140 * if no {@code Action} is set.
1141 *
1142 * @return the {@code Action} for this {@code ActionEvent}
1143 * source, or {@code null}
1144 * @since 1.3
1145 * @see Action
1146 * @see #setAction
1147 */
1148 public Action getAction() {
1149 return action;
1150 }
1151
1152 /**
1153 * Sets the properties on this button to match those in the specified
1154 * {@code Action}. Refer to <a href="Action.html#buttonActions">
1155 * Swing Components Supporting {@code Action}</a> for more
1156 * details as to which properties this sets.
1157 *
1158 * @param a the {@code Action} from which to get the properties,
1159 * or {@code null}
1160 * @since 1.3
1161 * @see Action
1162 * @see #setAction
1163 */
1164 protected void configurePropertiesFromAction(Action a) {
1165 setMnemonicFromAction(a);
1166 setTextFromAction(a, false);
1167 AbstractAction.setToolTipTextFromAction(this, a);
1168 setIconFromAction(a);
1169 setActionCommandFromAction(a);
1170 AbstractAction.setEnabledFromAction(this, a);
1171 if (AbstractAction.hasSelectedKey(a) &&
1172 shouldUpdateSelectedStateFromAction()) {
1173 setSelectedFromAction(a);
1174 }
1175 setDisplayedMnemonicIndexFromAction(a, false);
1176 }
1177
1178 void clientPropertyChanged(Object key, Object oldValue,
1179 Object newValue) {
1188
1189 /**
1190 * Button subclasses that support mirroring the selected state from
1191 * the action should override this to return true. AbstractButton's
1192 * implementation returns false.
1193 */
1194 boolean shouldUpdateSelectedStateFromAction() {
1195 return false;
1196 }
1197
1198 /**
1199 * Updates the button's state in response to property changes in the
1200 * associated action. This method is invoked from the
1201 * {@code PropertyChangeListener} returned from
1202 * {@code createActionPropertyChangeListener}. Subclasses do not normally
1203 * need to invoke this. Subclasses that support additional {@code Action}
1204 * properties should override this and
1205 * {@code configurePropertiesFromAction}.
1206 * <p>
1207 * Refer to the table at <a href="Action.html#buttonActions">
1208 * Swing Components Supporting {@code Action}</a> for a list of
1209 * the properties this method sets.
1210 *
1211 * @param action the {@code Action} associated with this button
1212 * @param propertyName the name of the property that changed
1213 * @since 1.6
1214 * @see Action
1215 * @see #configurePropertiesFromAction
1216 */
1217 protected void actionPropertyChanged(Action action, String propertyName) {
1218 if (propertyName == Action.NAME) {
1219 setTextFromAction(action, true);
1220 } else if (propertyName == "enabled") {
1221 AbstractAction.setEnabledFromAction(this, action);
1222 } else if (propertyName == Action.SHORT_DESCRIPTION) {
1223 AbstractAction.setToolTipTextFromAction(this, action);
1224 } else if (propertyName == Action.SMALL_ICON) {
1225 smallIconChanged(action);
1226 } else if (propertyName == Action.MNEMONIC_KEY) {
1227 setMnemonicFromAction(action);
1228 } else if (propertyName == Action.ACTION_COMMAND_KEY) {
1229 setActionCommandFromAction(action);
1230 } else if (propertyName == Action.SELECTED_KEY &&
1231 AbstractAction.hasSelectedKey(action) &&
1313 if (a != null) {
1314 selected = AbstractAction.isSelected(a);
1315 }
1316 if (selected != isSelected()) {
1317 // This won't notify ActionListeners, but that should be
1318 // ok as the change is coming from the Action.
1319 setSelected(selected);
1320 // Make sure the change actually took effect
1321 if (!selected && isSelected()) {
1322 if (getModel() instanceof DefaultButtonModel) {
1323 ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup();
1324 if (group != null) {
1325 group.clearSelection();
1326 }
1327 }
1328 }
1329 }
1330 }
1331
1332 /**
1333 * Creates and returns a {@code PropertyChangeListener} that is
1334 * responsible for listening for changes from the specified
1335 * {@code Action} and updating the appropriate properties.
1336 * <p>
1337 * <b>Warning:</b> If you subclass this do not create an anonymous
1338 * inner class. If you do the lifetime of the button will be tied to
1339 * that of the {@code Action}.
1340 *
1341 * @param a the button's action
1342 * @return the {@code PropertyChangeListener}
1343 * @since 1.3
1344 * @see Action
1345 * @see #setAction
1346 */
1347 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1348 return createActionPropertyChangeListener0(a);
1349 }
1350
1351
1352 PropertyChangeListener createActionPropertyChangeListener0(Action a) {
1353 return new ButtonActionPropertyChangeListener(this, a);
1354 }
1355
1356 @SuppressWarnings("serial")
1357 private static class ButtonActionPropertyChangeListener
1358 extends ActionPropertyChangeListener<AbstractButton> {
1359 ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
1360 super(b, a);
1361 }
1362 protected void actionPropertyChanged(AbstractButton button,
1363 Action action,
1364 PropertyChangeEvent e) {
1365 if (AbstractAction.shouldReconfigure(e)) {
1366 button.configurePropertiesFromAction(action);
1367 } else {
1368 button.actionPropertyChanged(action, e.getPropertyName());
1369 }
1370 }
1371 }
1372
1373 /**
1374 * Gets the {@code borderPainted} property.
1375 *
1376 * @return the value of the {@code borderPainted} property
1377 * @see #setBorderPainted
1378 */
1379 public boolean isBorderPainted() {
1380 return paintBorder;
1381 }
1382
1383 /**
1384 * Sets the {@code borderPainted} property.
1385 * If {@code true} and the button has a border,
1386 * the border is painted. The default value for the
1387 * {@code borderPainted} property is {@code true}.
1388 * <p>
1389 * Some look and feels might not support
1390 * the {@code borderPainted} property,
1391 * in which case they ignore this.
1392 *
1393 * @param b if true and border property is not {@code null},
1394 * the border is painted
1395 * @see #isBorderPainted
1396 * @beaninfo
1397 * bound: true
1398 * attribute: visualUpdate true
1399 * description: Whether the border should be painted.
1400 */
1401 public void setBorderPainted(boolean b) {
1402 boolean oldValue = paintBorder;
1403 paintBorder = b;
1404 borderPaintedSet = true;
1405 firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
1406 if (b != oldValue) {
1407 revalidate();
1408 repaint();
1409 }
1410 }
1411
1412 /**
1413 * Paint the button's border if {@code BorderPainted}
1414 * property is true and the button has a border.
1415 * @param g the {@code Graphics} context in which to paint
1416 *
1417 * @see #paint
1418 * @see #setBorder
1419 */
1420 protected void paintBorder(Graphics g) {
1421 if (isBorderPainted()) {
1422 super.paintBorder(g);
1423 }
1424 }
1425
1426 /**
1427 * Gets the {@code paintFocus} property.
1428 *
1429 * @return the {@code paintFocus} property
1430 * @see #setFocusPainted
1431 */
1432 public boolean isFocusPainted() {
1433 return paintFocus;
1434 }
1435
1436 /**
1437 * Sets the {@code paintFocus} property, which must
1438 * be {@code true} for the focus state to be painted.
1439 * The default value for the {@code paintFocus} property
1440 * is {@code true}.
1441 * Some look and feels might not paint focus state;
1442 * they will ignore this property.
1443 *
1444 * @param b if {@code true}, the focus state should be painted
1445 * @see #isFocusPainted
1446 * @beaninfo
1447 * bound: true
1448 * attribute: visualUpdate true
1449 * description: Whether focus should be painted
1450 */
1451 public void setFocusPainted(boolean b) {
1452 boolean oldValue = paintFocus;
1453 paintFocus = b;
1454 firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
1455 if (b != oldValue && isFocusOwner()) {
1456 revalidate();
1457 repaint();
1458 }
1459 }
1460
1461 /**
1462 * Gets the {@code contentAreaFilled} property.
1463 *
1464 * @return the {@code contentAreaFilled} property
1465 * @see #setContentAreaFilled
1466 */
1467 public boolean isContentAreaFilled() {
1468 return contentAreaFilled;
1469 }
1470
1471 /**
1472 * Sets the {@code contentAreaFilled} property.
1473 * If {@code true} the button will paint the content
1474 * area. If you wish to have a transparent button, such as
1475 * an icon only button, for example, then you should set
1476 * this to {@code false}. Do not call {@code setOpaque(false)}.
1477 * The default value for the {@code contentAreaFilled}
1478 * property is {@code true}.
1479 * <p>
1480 * This function may cause the component's opaque property to change.
1481 * <p>
1482 * The exact behavior of calling this function varies on a
1483 * component-by-component and L&F-by-L&F basis.
1484 *
1485 * @param b if true, the content should be filled; if false
1486 * the content area is not filled
1487 * @see #isContentAreaFilled
1488 * @see #setOpaque
1489 * @beaninfo
1490 * bound: true
1491 * attribute: visualUpdate true
1492 * description: Whether the button should paint the content area
1493 * or leave it transparent.
1494 */
1495 public void setContentAreaFilled(boolean b) {
1496 boolean oldValue = contentAreaFilled;
1497 contentAreaFilled = b;
1498 contentAreaFilledSet = true;
1499 firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
1500 if (b != oldValue) {
1501 repaint();
1502 }
1503 }
1504
1505 /**
1506 * Gets the {@code rolloverEnabled} property.
1507 *
1508 * @return the value of the {@code rolloverEnabled} property
1509 * @see #setRolloverEnabled
1510 */
1511 public boolean isRolloverEnabled() {
1512 return rolloverEnabled;
1513 }
1514
1515 /**
1516 * Sets the {@code rolloverEnabled} property, which
1517 * must be {@code true} for rollover effects to occur.
1518 * The default value for the {@code rolloverEnabled}
1519 * property is {@code false}.
1520 * Some look and feels might not implement rollover effects;
1521 * they will ignore this property.
1522 *
1523 * @param b if {@code true}, rollover effects should be painted
1524 * @see #isRolloverEnabled
1525 * @beaninfo
1526 * bound: true
1527 * attribute: visualUpdate true
1528 * description: Whether rollover effects should be enabled.
1529 */
1530 public void setRolloverEnabled(boolean b) {
1531 boolean oldValue = rolloverEnabled;
1532 rolloverEnabled = b;
1533 rolloverEnabledSet = true;
1534 firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
1535 if (b != oldValue) {
1536 repaint();
1537 }
1538 }
1539
1540 /**
1541 * Returns the keyboard mnemonic from the current model.
1542 * @return the keyboard mnemonic from the model
1543 */
1544 public int getMnemonic() {
1545 return mnemonic;
1546 }
1547
1548 /**
1549 * Sets the keyboard mnemonic on the current model.
1550 * The mnemonic is the key which when combined with the look and feel's
1551 * mouseless modifier (usually Alt) will activate this button
1552 * if focus is contained somewhere within this button's ancestor
1553 * window.
1554 * <p>
1555 * A mnemonic must correspond to a single key on the keyboard
1556 * and should be specified using one of the {@code VK_XXX}
1557 * keycodes defined in {@code java.awt.event.KeyEvent}.
1558 * These codes and the wider array of codes for international
1559 * keyboards may be obtained through
1560 * {@code java.awt.event.KeyEvent.getExtendedKeyCodeForChar}.
1561 * Mnemonics are case-insensitive, therefore a key event
1562 * with the corresponding keycode would cause the button to be
1563 * activated whether or not the Shift modifier was pressed.
1564 * <p>
1565 * If the character defined by the mnemonic is found within
1566 * the button's label string, the first occurrence of it
1567 * will be underlined to indicate the mnemonic to the user.
1568 *
1569 * @param mnemonic the key code which represents the mnemonic
1570 * @see java.awt.event.KeyEvent
1571 * @see #setDisplayedMnemonicIndex
1572 *
1573 * @beaninfo
1574 * bound: true
1575 * attribute: visualUpdate true
1576 * description: the keyboard character mnemonic
1577 */
1578 public void setMnemonic(int mnemonic) {
1579 int oldValue = getMnemonic();
1580 model.setMnemonic(mnemonic);
1581 updateMnemonicProperties();
1582 }
1583
1584 /**
1585 * This method is now obsolete, please use {@code setMnemonic(int)}
1586 * to set the mnemonic for a button. This method is only designed
1587 * to handle character values which fall between 'a' and 'z' or
1588 * 'A' and 'Z'.
1589 *
1590 * @param mnemonic a char specifying the mnemonic value
1591 * @see #setMnemonic(int)
1592 * @beaninfo
1593 * bound: true
1594 * attribute: visualUpdate true
1595 * description: the keyboard character mnemonic
1596 */
1597 public void setMnemonic(char mnemonic) {
1598 int vk = (int) mnemonic;
1599 if(vk >= 'a' && vk <='z')
1600 vk -= ('a' - 'A');
1601 setMnemonic(vk);
1602 }
1603
1604 /**
1605 * Provides a hint to the look and feel as to which character in the
1606 * text should be decorated to represent the mnemonic. Not all look and
1607 * feels may support this. A value of -1 indicates either there is no
1608 * mnemonic, the mnemonic character is not contained in the string, or
1609 * the developer does not wish the mnemonic to be displayed.
1610 * <p>
1611 * The value of this is updated as the properties relating to the
1612 * mnemonic change (such as the mnemonic itself, the text...).
1613 * You should only ever have to call this if
1614 * you do not wish the default character to be underlined. For example, if
1615 * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
1616 * to be decorated, as 'Save <u>A</u>s', you would have to invoke
1617 * {@code setDisplayedMnemonicIndex(5)} after invoking
1618 * {@code setMnemonic(KeyEvent.VK_A)}.
1619 *
1620 * @since 1.4
1621 * @param index Index into the String to underline
1622 * @exception IllegalArgumentException will be thrown if {@code index}
1623 * is >= length of the text, or < -1
1624 * @see #getDisplayedMnemonicIndex
1625 *
1626 * @beaninfo
1627 * bound: true
1628 * attribute: visualUpdate true
1629 * description: the index into the String to draw the keyboard character
1630 * mnemonic at
1631 */
1632 public void setDisplayedMnemonicIndex(int index)
1633 throws IllegalArgumentException {
1634 int oldValue = mnemonicIndex;
1635 if (index == -1) {
1636 mnemonicIndex = -1;
1637 } else {
1638 String text = getText();
1639 int textLength = (text == null) ? 0 : text.length();
1640 if (index < -1 || index >= textLength) { // index out of range
1641 throw new IllegalArgumentException("index == " + index);
1642 }
1715 }
1716 this.multiClickThreshhold = threshhold;
1717 }
1718
1719 /**
1720 * Gets the amount of time (in milliseconds) required between
1721 * mouse press events for the button to generate the corresponding
1722 * action events.
1723 *
1724 * @see #setMultiClickThreshhold
1725 * @return the amount of time required between mouse press events
1726 * to generate corresponding action events
1727 * @since 1.4
1728 */
1729 public long getMultiClickThreshhold() {
1730 return multiClickThreshhold;
1731 }
1732
1733 /**
1734 * Returns the model that this button represents.
1735 * @return the {@code model} property
1736 * @see #setModel
1737 */
1738 public ButtonModel getModel() {
1739 return model;
1740 }
1741
1742 /**
1743 * Sets the model that this button represents.
1744 * @param newModel the new {@code ButtonModel}
1745 * @see #getModel
1746 * @beaninfo
1747 * bound: true
1748 * description: Model that the Button uses.
1749 */
1750 public void setModel(ButtonModel newModel) {
1751
1752 ButtonModel oldModel = getModel();
1753
1754 if (oldModel != null) {
1755 oldModel.removeChangeListener(changeListener);
1756 oldModel.removeActionListener(actionListener);
1757 oldModel.removeItemListener(itemListener);
1758 changeListener = null;
1759 actionListener = null;
1760 itemListener = null;
1761 }
1762
1763 model = newModel;
1764
1785 firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
1786 if (newModel != oldModel) {
1787 revalidate();
1788 repaint();
1789 }
1790 }
1791
1792
1793 /**
1794 * Returns the L&F object that renders this component.
1795 * @return the ButtonUI object
1796 * @see #setUI
1797 */
1798 public ButtonUI getUI() {
1799 return (ButtonUI) ui;
1800 }
1801
1802
1803 /**
1804 * Sets the L&F object that renders this component.
1805 * @param ui the {@code ButtonUI} L&F object
1806 * @see #getUI
1807 * @beaninfo
1808 * bound: true
1809 * hidden: true
1810 * attribute: visualUpdate true
1811 * description: The UI object that implements the LookAndFeel.
1812 */
1813 public void setUI(ButtonUI ui) {
1814 super.setUI(ui);
1815 // disabled icons are generated by the LF so they should be unset here
1816 if (disabledIcon instanceof UIResource) {
1817 setDisabledIcon(null);
1818 }
1819 if (disabledSelectedIcon instanceof UIResource) {
1820 setDisabledSelectedIcon(null);
1821 }
1822 }
1823
1824
1825 /**
1826 * Resets the UI property to a value from the current look
1827 * and feel. Subtypes of {@code AbstractButton}
1828 * should override this to update the UI. For
1829 * example, {@code JButton} might do the following:
1830 * <pre>
1831 * setUI((ButtonUI)UIManager.getUI(
1832 * "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
1833 * </pre>
1834 */
1835 public void updateUI() {
1836 }
1837
1838 /**
1839 * Adds the specified component to this container at the specified
1840 * index, refer to
1841 * {@link java.awt.Container#addImpl(Component, Object, int)}
1842 * for a complete description of this method.
1843 *
1844 * @param comp the component to be added
1845 * @param constraints an object expressing layout constraints
1846 * for this component
1847 * @param index the position in the container's list at which to
1848 * insert the component, where {@code -1}
1849 * means append to the end
1850 * @exception IllegalArgumentException if {@code index} is invalid
1851 * @exception IllegalArgumentException if adding the container's parent
1852 * to itself
1853 * @exception IllegalArgumentException if adding a window to a container
1854 * @since 1.5
1855 */
1856 protected void addImpl(Component comp, Object constraints, int index) {
1857 if (!setLayout) {
1858 setLayout(new OverlayLayout(this));
1859 }
1860 super.addImpl(comp, constraints, index);
1861 }
1862
1863 /**
1864 * Sets the layout manager for this container, refer to
1865 * {@link java.awt.Container#setLayout(LayoutManager)}
1866 * for a complete description of this method.
1867 *
1868 * @param mgr the specified layout manager
1869 * @since 1.5
1870 */
1871 public void setLayout(LayoutManager mgr) {
1872 setLayout = true;
1873 super.setLayout(mgr);
1874 }
1875
1876 /**
1877 * Adds a {@code ChangeListener} to the button.
1878 * @param l the listener to be added
1879 */
1880 public void addChangeListener(ChangeListener l) {
1881 listenerList.add(ChangeListener.class, l);
1882 }
1883
1884 /**
1885 * Removes a ChangeListener from the button.
1886 * @param l the listener to be removed
1887 */
1888 public void removeChangeListener(ChangeListener l) {
1889 listenerList.remove(ChangeListener.class, l);
1890 }
1891
1892 /**
1893 * Returns an array of all the {@code ChangeListener}s added
1894 * to this AbstractButton with addChangeListener().
1895 *
1896 * @return all of the {@code ChangeListener}s added or an empty
1897 * array if no listeners have been added
1898 * @since 1.4
1899 */
1900 public ChangeListener[] getChangeListeners() {
1901 return listenerList.getListeners(ChangeListener.class);
1902 }
1903
1904 /**
1905 * Notifies all listeners that have registered interest for
1906 * notification on this event type. The event instance
1907 * is lazily created.
1908 * @see EventListenerList
1909 */
1910 protected void fireStateChanged() {
1911 // Guaranteed to return a non-null array
1912 Object[] listeners = listenerList.getListenerList();
1913 // Process the listeners last to first, notifying
1914 // those that are interested in this event
1915 for (int i = listeners.length-2; i>=0; i-=2) {
1916 if (listeners[i]==ChangeListener.class) {
1917 // Lazily create the event:
1918 if (changeEvent == null)
1919 changeEvent = new ChangeEvent(this);
1920 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
1921 }
1922 }
1923 }
1924
1925 /**
1926 * Adds an {@code ActionListener} to the button.
1927 * @param l the {@code ActionListener} to be added
1928 */
1929 public void addActionListener(ActionListener l) {
1930 listenerList.add(ActionListener.class, l);
1931 }
1932
1933 /**
1934 * Removes an {@code ActionListener} from the button.
1935 * If the listener is the currently set {@code Action}
1936 * for the button, then the {@code Action}
1937 * is set to {@code null}.
1938 *
1939 * @param l the listener to be removed
1940 */
1941 public void removeActionListener(ActionListener l) {
1942 if ((l != null) && (getAction() == l)) {
1943 setAction(null);
1944 } else {
1945 listenerList.remove(ActionListener.class, l);
1946 }
1947 }
1948
1949 /**
1950 * Returns an array of all the {@code ActionListener}s added
1951 * to this AbstractButton with addActionListener().
1952 *
1953 * @return all of the {@code ActionListener}s added or an empty
1954 * array if no listeners have been added
1955 * @since 1.4
1956 */
1957 public ActionListener[] getActionListeners() {
1958 return listenerList.getListeners(ActionListener.class);
1959 }
1960
1961 /**
1962 * Subclasses that want to handle {@code ChangeEvents} differently
1963 * can override this to return another {@code ChangeListener}
1964 * implementation.
1965 *
1966 * @return the new {@code ChangeListener}
1967 */
1968 protected ChangeListener createChangeListener() {
1969 return getHandler();
1970 }
1971
1972 /**
1973 * Extends {@code ChangeListener} to be serializable.
1974 * <p>
1975 * <strong>Warning:</strong>
1976 * Serialized objects of this class will not be compatible with
1977 * future Swing releases. The current serialization support is
1978 * appropriate for short term storage or RMI between applications running
1979 * the same version of Swing. As of 1.4, support for long term storage
1980 * of all JavaBeans™
1981 * has been added to the {@code java.beans} package.
1982 * Please see {@link java.beans.XMLEncoder}.
1983 */
1984 @SuppressWarnings("serial")
1985 protected class ButtonChangeListener implements ChangeListener, Serializable {
1986 // NOTE: This class is NOT used, instead the functionality has
1987 // been moved to Handler.
1988 ButtonChangeListener() {
1989 }
1990
1991 public void stateChanged(ChangeEvent e) {
1992 getHandler().stateChanged(e);
1993 }
1994 }
1995
1996
1997 /**
1998 * Notifies all listeners that have registered interest for
1999 * notification on this event type. The event instance
2000 * is lazily created using the {@code event}
2001 * parameter.
2002 *
2003 * @param event the {@code ActionEvent} object
2004 * @see EventListenerList
2005 */
2006 protected void fireActionPerformed(ActionEvent event) {
2007 // Guaranteed to return a non-null array
2008 Object[] listeners = listenerList.getListenerList();
2009 ActionEvent e = null;
2010 // Process the listeners last to first, notifying
2011 // those that are interested in this event
2012 for (int i = listeners.length-2; i>=0; i-=2) {
2013 if (listeners[i]==ActionListener.class) {
2014 // Lazily create the event:
2015 if (e == null) {
2016 String actionCommand = event.getActionCommand();
2017 if(actionCommand == null) {
2018 actionCommand = getActionCommand();
2019 }
2020 e = new ActionEvent(AbstractButton.this,
2021 ActionEvent.ACTION_PERFORMED,
2022 actionCommand,
2023 event.getWhen(),
2024 event.getModifiers());
2025 }
2026 ((ActionListener)listeners[i+1]).actionPerformed(e);
2027 }
2028 }
2029 }
2030
2031 /**
2032 * Notifies all listeners that have registered interest for
2033 * notification on this event type. The event instance
2034 * is lazily created using the {@code event} parameter.
2035 *
2036 * @param event the {@code ItemEvent} object
2037 * @see EventListenerList
2038 */
2039 protected void fireItemStateChanged(ItemEvent event) {
2040 // Guaranteed to return a non-null array
2041 Object[] listeners = listenerList.getListenerList();
2042 ItemEvent e = null;
2043 // Process the listeners last to first, notifying
2044 // those that are interested in this event
2045 for (int i = listeners.length-2; i>=0; i-=2) {
2046 if (listeners[i]==ItemListener.class) {
2047 // Lazily create the event:
2048 if (e == null) {
2049 e = new ItemEvent(AbstractButton.this,
2050 ItemEvent.ITEM_STATE_CHANGED,
2051 AbstractButton.this,
2052 event.getStateChange());
2053 }
2054 ((ItemListener)listeners[i+1]).itemStateChanged(e);
2055 }
2056 }
2092 }
2093
2094
2095 /**
2096 * Enables (or disables) the button.
2097 * @param b true to enable the button, otherwise false
2098 */
2099 public void setEnabled(boolean b) {
2100 if (!b && model.isRollover()) {
2101 model.setRollover(false);
2102 }
2103 super.setEnabled(b);
2104 model.setEnabled(b);
2105 }
2106
2107 // *** Deprecated java.awt.Button APIs below *** //
2108
2109 /**
2110 * Returns the label text.
2111 *
2112 * @return a {@code String} containing the label
2113 * @deprecated - Replaced by {@code getText}
2114 */
2115 @Deprecated
2116 public String getLabel() {
2117 return getText();
2118 }
2119
2120 /**
2121 * Sets the label text.
2122 *
2123 * @param label a {@code String} containing the text
2124 * @deprecated - Replaced by {@code setText(text)}
2125 * @beaninfo
2126 * bound: true
2127 * description: Replace by setText(text)
2128 */
2129 @Deprecated
2130 public void setLabel(String label) {
2131 setText(label);
2132 }
2133
2134 /**
2135 * Adds an {@code ItemListener} to the {@code checkbox}.
2136 * @param l the {@code ItemListener} to be added
2137 */
2138 public void addItemListener(ItemListener l) {
2139 listenerList.add(ItemListener.class, l);
2140 }
2141
2142 /**
2143 * Removes an {@code ItemListener} from the button.
2144 * @param l the {@code ItemListener} to be removed
2145 */
2146 public void removeItemListener(ItemListener l) {
2147 listenerList.remove(ItemListener.class, l);
2148 }
2149
2150 /**
2151 * Returns an array of all the {@code ItemListener}s added
2152 * to this AbstractButton with addItemListener().
2153 *
2154 * @return all of the {@code ItemListener}s added or an empty
2155 * array if no listeners have been added
2156 * @since 1.4
2157 */
2158 public ItemListener[] getItemListeners() {
2159 return listenerList.getListeners(ItemListener.class);
2160 }
2161
2162 /**
2163 * Returns an array (length 1) containing the label or
2164 * {@code null} if the button is not selected.
2165 *
2166 * @return an array containing 1 Object: the text of the button,
2167 * if the item is selected; otherwise {@code null}
2168 */
2169 public Object[] getSelectedObjects() {
2170 if (isSelected() == false) {
2171 return null;
2172 }
2173 Object[] selectedObjects = new Object[1];
2174 selectedObjects[0] = getText();
2175 return selectedObjects;
2176 }
2177
2178 /**
2179 * Initialization of the {@code AbstractButton}.
2180 *
2181 * @param text the text of the button
2182 * @param icon the Icon image to display on the button
2183 */
2184 protected void init(String text, Icon icon) {
2185 if(text != null) {
2186 setText(text);
2187 }
2188
2189 if(icon != null) {
2190 setIcon(icon);
2191 }
2192
2193 // Set the UI
2194 updateUI();
2195
2196 setAlignmentX(LEFT_ALIGNMENT);
2197 setAlignmentY(CENTER_ALIGNMENT);
2198 }
2199
2200
2201 /**
2202 * This is overridden to return false if the current {@code Icon}'s
2203 * {@code Image} is not equal to the
2204 * passed in {@code Image img}.
2205 *
2206 * @param img the {@code Image} to be compared
2207 * @param infoflags flags used to repaint the button when the image
2208 * is updated and which determine how much is to be painted
2209 * @param x the x coordinate
2210 * @param y the y coordinate
2211 * @param w the width
2212 * @param h the height
2213 * @see java.awt.image.ImageObserver
2214 * @see java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
2215 */
2216 public boolean imageUpdate(Image img, int infoflags,
2217 int x, int y, int w, int h) {
2218 Icon iconDisplayed = null;
2219
2220 if (!model.isEnabled()) {
2221 if (model.isSelected()) {
2222 iconDisplayed = getDisabledSelectedIcon();
2223 } else {
2224 iconDisplayed = getDisabledIcon();
2225 }
2226 } else if (model.isPressed() && model.isArmed()) {
2258 if (!rolloverEnabledSet) {
2259 setRolloverEnabled(((Boolean)value).booleanValue());
2260 rolloverEnabledSet = false;
2261 }
2262 } else if (propertyName == "iconTextGap") {
2263 if (!iconTextGapSet) {
2264 setIconTextGap(((Number)value).intValue());
2265 iconTextGapSet = false;
2266 }
2267 } else if (propertyName == "contentAreaFilled") {
2268 if (!contentAreaFilledSet) {
2269 setContentAreaFilled(((Boolean)value).booleanValue());
2270 contentAreaFilledSet = false;
2271 }
2272 } else {
2273 super.setUIProperty(propertyName, value);
2274 }
2275 }
2276
2277 /**
2278 * Returns a string representation of this {@code AbstractButton}.
2279 * This method
2280 * is intended to be used only for debugging purposes, and the
2281 * content and format of the returned string may vary between
2282 * implementations. The returned string may be empty but may not
2283 * be {@code null}.
2284 * <P>
2285 * Overriding {@code paramString} to provide information about the
2286 * specific new aspects of the JFC components.
2287 *
2288 * @return a string representation of this {@code AbstractButton}
2289 */
2290 protected String paramString() {
2291 String defaultIconString = ((defaultIcon != null)
2292 && (defaultIcon != this) ?
2293 defaultIcon.toString() : "");
2294 String pressedIconString = ((pressedIcon != null)
2295 && (pressedIcon != this) ?
2296 pressedIcon.toString() : "");
2297 String disabledIconString = ((disabledIcon != null)
2298 && (disabledIcon != this) ?
2299 disabledIcon.toString() : "");
2300 String selectedIconString = ((selectedIcon != null)
2301 && (selectedIcon != this) ?
2302 selectedIcon.toString() : "");
2303 String disabledSelectedIconString = ((disabledSelectedIcon != null) &&
2304 (disabledSelectedIcon != this) ?
2305 disabledSelectedIcon.toString()
2306 : "");
2307 String rolloverIconString = ((rolloverIcon != null)
2308 && (rolloverIcon != this) ?
2373 fireItemStateChanged(event);
2374 if (shouldUpdateSelectedStateFromAction()) {
2375 Action action = getAction();
2376 if (action != null && AbstractAction.hasSelectedKey(action)) {
2377 boolean selected = isSelected();
2378 boolean isActionSelected = AbstractAction.isSelected(
2379 action);
2380 if (isActionSelected != selected) {
2381 action.putValue(Action.SELECTED_KEY, selected);
2382 }
2383 }
2384 }
2385 }
2386 }
2387
2388 ///////////////////
2389 // Accessibility support
2390 ///////////////////
2391 /**
2392 * This class implements accessibility support for the
2393 * {@code AbstractButton} class. It provides an implementation of the
2394 * Java Accessibility API appropriate to button and menu item
2395 * user-interface elements.
2396 * <p>
2397 * <strong>Warning:</strong>
2398 * Serialized objects of this class will not be compatible with
2399 * future Swing releases. The current serialization support is
2400 * appropriate for short term storage or RMI between applications running
2401 * the same version of Swing. As of 1.4, support for long term storage
2402 * of all JavaBeans™
2403 * has been added to the {@code java.beans} package.
2404 * Please see {@link java.beans.XMLEncoder}.
2405 * @since 1.4
2406 */
2407 @SuppressWarnings("serial") // Same-version serialization only
2408 protected abstract class AccessibleAbstractButton
2409 extends AccessibleJComponent implements AccessibleAction,
2410 AccessibleValue, AccessibleText, AccessibleExtendedComponent {
2411
2412 /**
2413 * Returns the accessible name of this object.
2414 *
2415 * @return the localized name of the object -- can be
2416 * {@code null} if this
2417 * object does not have a name
2418 */
2419 public String getAccessibleName() {
2420 String name = accessibleName;
2421
2422 if (name == null) {
2423 name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
2424 }
2425 if (name == null) {
2426 name = AbstractButton.this.getText();
2427 }
2428 if (name == null) {
2429 name = super.getAccessibleName();
2430 }
2431 return name;
2432 }
2433
2434 /**
2435 * Get the AccessibleIcons associated with this object if one
2436 * or more exist. Otherwise return null.
3080
3081 ButtonKeyBinding(int mnemonic) {
3082 this.mnemonic = mnemonic;
3083 }
3084
3085 /**
3086 * Returns the number of key bindings for this object
3087 *
3088 * @return the zero-based number of key bindings for this object
3089 */
3090 public int getAccessibleKeyBindingCount() {
3091 return 1;
3092 }
3093
3094 /**
3095 * Returns a key binding for this object. The value returned is an
3096 * java.lang.Object which must be cast to appropriate type depending
3097 * on the underlying implementation of the key. For example, if the
3098 * Object returned is a javax.swing.KeyStroke, the user of this
3099 * method should do the following:
3100 * <pre>{@code
3101 * Component c = <get the component that has the key bindings>
3102 * AccessibleContext ac = c.getAccessibleContext();
3103 * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
3104 * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
3105 * Object o = akb.getAccessibleKeyBinding(i);
3106 * if (o instanceof javax.swing.KeyStroke) {
3107 * javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
3108 * <do something with the key binding>
3109 * }
3110 * }
3111 * }</pre>
3112 *
3113 * @param i zero-based index of the key bindings
3114 * @return a javax.lang.Object which specifies the key binding
3115 * @exception IllegalArgumentException if the index is
3116 * out of bounds
3117 * @see #getAccessibleKeyBindingCount
3118 */
3119 public java.lang.Object getAccessibleKeyBinding(int i) {
3120 if (i != 0) {
3121 throw new IllegalArgumentException();
3122 }
3123 return KeyStroke.getKeyStroke(mnemonic, 0);
3124 }
3125 }
3126 }
3127 }
|