View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package javax.swing;
26  
27  import java.awt.*;
28  import java.awt.event.*;
29  import java.awt.image.*;
30  import java.text.*;
31  import java.awt.geom.*;
32  import java.beans.PropertyChangeEvent;
33  import java.beans.PropertyChangeListener;
34  import java.beans.Transient;
35  import java.util.Enumeration;
36  import java.util.Vector;
37  import java.io.Serializable;
38  import javax.swing.event.*;
39  import javax.swing.border.*;
40  import javax.swing.plaf.*;
41  import javax.accessibility.*;
42  import javax.swing.text.*;
43  import javax.swing.text.html.*;
44  import javax.swing.plaf.basic.*;
45  import java.util.*;
46  
47  /**
48   * Defines common behaviors for buttons and menu items.
49   * <p>
50   * Buttons can be configured, and to some degree controlled, by
51   * <code><a href="Action.html">Action</a></code>s.  Using an
52   * <code>Action</code> with a button has many benefits beyond directly
53   * configuring a button.  Refer to <a href="Action.html#buttonActions">
54   * Swing Components Supporting <code>Action</code></a> for more
55   * details, and you can find more information in <a
56   * href="http://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&trade;
70   * has been added to the <code>java.beans</code> package.
71   * Please see {@link java.beans.XMLEncoder}.
72   *
73   * @author Jeff Dinkins
74   */
75  public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {
76  
77      // *********************************
78      // ******* Button properties *******
79      // *********************************
80  
81      /** Identifies a change in the button model. */
82      public static final String MODEL_CHANGED_PROPERTY = "model";
83      /** Identifies a change in the button's text. */
84      public static final String TEXT_CHANGED_PROPERTY = "text";
85      /** Identifies a change to the button's mnemonic. */
86      public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
87  
88      // Text positioning and alignment
89      /** Identifies a change in the button's margins. */
90      public static final String MARGIN_CHANGED_PROPERTY = "margin";
91      /** Identifies a change in the button's vertical alignment. */
92      public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = "verticalAlignment";
93      /** Identifies a change in the button's horizontal alignment. */
94      public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = "horizontalAlignment";
95  
96      /** Identifies a change in the button's vertical text position. */
97      public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = "verticalTextPosition";
98      /** Identifies a change in the button's horizontal text position. */
99      public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = "horizontalTextPosition";
100 
101     // Paint options
102     /**
103      * Identifies a change to having the border drawn,
104      * or having it not drawn.
105      */
106     public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
107     /**
108      * Identifies a change to having the border highlighted when focused,
109      * or not.
110      */
111     public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
112     /**
113      * Identifies a change from rollover enabled to disabled or back
114      * to enabled.
115      */
116     public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = "rolloverEnabled";
117     /**
118      * Identifies a change to having the button paint the content area.
119      */
120     public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = "contentAreaFilled";
121 
122     // Icons
123     /** Identifies a change to the icon that represents the button. */
124     public static final String ICON_CHANGED_PROPERTY = "icon";
125 
126     /**
127      * Identifies a change to the icon used when the button has been
128      * pressed.
129      */
130     public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
131     /**
132      * Identifies a change to the icon used when the button has
133      * been selected.
134      */
135     public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
136 
137     /**
138      * Identifies a change to the icon used when the cursor is over
139      * the button.
140      */
141     public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
142     /**
143      * Identifies a change to the icon used when the cursor is
144      * over the button and it has been selected.
145      */
146     public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = "rolloverSelectedIcon";
147 
148     /**
149      * Identifies a change to the icon used when the button has
150      * been disabled.
151      */
152     public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
153     /**
154      * Identifies a change to the icon used when the button has been
155      * disabled and selected.
156      */
157     public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = "disabledSelectedIcon";
158 
159 
160     /** The data model that determines the button's state. */
161     protected ButtonModel model                = null;
162 
163     private String     text                    = ""; // for BeanBox
164     private Insets     margin                  = null;
165     private Insets     defaultMargin           = null;
166 
167     // Button icons
168     // PENDING(jeff) - hold icons in an array
169     private Icon       defaultIcon             = null;
170     private Icon       pressedIcon             = null;
171     private Icon       disabledIcon            = null;
172 
173     private Icon       selectedIcon            = null;
174     private Icon       disabledSelectedIcon    = null;
175 
176     private Icon       rolloverIcon            = null;
177     private Icon       rolloverSelectedIcon    = null;
178 
179     // Display properties
180     private boolean    paintBorder             = true;
181     private boolean    paintFocus              = true;
182     private boolean    rolloverEnabled         = false;
183     private boolean    contentAreaFilled         = true;
184 
185     // Icon/Label Alignment
186     private int        verticalAlignment       = CENTER;
187     private int        horizontalAlignment     = CENTER;
188 
189     private int        verticalTextPosition    = CENTER;
190     private int        horizontalTextPosition  = TRAILING;
191 
192     private int        iconTextGap             = 4;
193 
194     private int        mnemonic;
195     private int        mnemonicIndex           = -1;
196 
197     private long       multiClickThreshhold    = 0;
198 
199     private boolean    borderPaintedSet        = false;
200     private boolean    rolloverEnabledSet      = false;
201     private boolean    iconTextGapSet          = false;
202     private boolean    contentAreaFilledSet    = false;
203 
204     // Whether or not we've set the LayoutManager.
205     private boolean setLayout = false;
206 
207     // This is only used by JButton, promoted to avoid an extra
208     // boolean field in JButton
209     boolean defaultCapable = true;
210 
211     /**
212      * Combined listeners: ActionListener, ChangeListener, ItemListener.
213      */
214     private Handler handler;
215 
216     /**
217      * The button model's <code>changeListener</code>.
218      */
219     protected ChangeListener changeListener = null;
220     /**
221      * The button model's <code>ActionListener</code>.
222      */
223     protected ActionListener actionListener = null;
224     /**
225      * The button model's <code>ItemListener</code>.
226      */
227     protected ItemListener itemListener = null;
228 
229     /**
230      * Only one <code>ChangeEvent</code> is needed per button
231      * instance since the
232      * event's only state is the source property.  The source of events
233      * generated is always "this".
234      */
235     protected transient ChangeEvent changeEvent;
236 
237     private boolean hideActionText = false;
238 
239     /**
240      * Sets the <code>hideActionText</code> property, which determines
241      * whether the button displays text from the <code>Action</code>.
242      * This is useful only if an <code>Action</code> has been
243      * installed on the button.
244      *
245      * @param hideActionText <code>true</code> if the button's
246      *                       <code>text</code> property should not reflect
247      *                       that of the <code>Action</code>; the default is
248      *                       <code>false</code>
249      * @see <a href="Action.html#buttonActions">Swing Components Supporting
250      *      <code>Action</code></a>
251      * @since 1.6
252      * @beaninfo
253      *        bound: true
254      *    expert: true
255      *  description: Whether the text of the button should come from
256      *               the <code>Action</code>.
257      */
258     public void setHideActionText(boolean hideActionText) {
259         if (hideActionText != this.hideActionText) {
260             this.hideActionText = hideActionText;
261             if (getAction() != null) {
262                 setTextFromAction(getAction(), false);
263             }
264             firePropertyChange("hideActionText", !hideActionText,
265                                hideActionText);
266         }
267     }
268 
269     /**
270      * Returns the value of the <code>hideActionText</code> property, which
271      * determines whether the button displays text from the
272      * <code>Action</code>.  This is useful only if an <code>Action</code>
273      * has been installed on the button.
274      *
275      * @return <code>true</code> if the button's <code>text</code>
276      *         property should not reflect that of the
277      *         <code>Action</code>; the default is <code>false</code>
278      * @since 1.6
279      */
280     public boolean getHideActionText() {
281         return hideActionText;
282     }
283 
284     /**
285      * Returns the button's text.
286      * @return the buttons text
287      * @see #setText
288      */
289     public String getText() {
290         return text;
291     }
292 
293     /**
294      * Sets the button's text.
295      * @param text the string used to set the text
296      * @see #getText
297      * @beaninfo
298      *        bound: true
299      *    preferred: true
300      *    attribute: visualUpdate true
301      *  description: The button's text.
302      */
303     public void setText(String text) {
304         String oldValue = this.text;
305         this.text = text;
306         firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);
307         updateDisplayedMnemonicIndex(text, getMnemonic());
308 
309         if (accessibleContext != null) {
310             accessibleContext.firePropertyChange(
311                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
312                 oldValue, text);
313         }
314         if (text == null || oldValue == null || !text.equals(oldValue)) {
315             revalidate();
316             repaint();
317         }
318     }
319 
320 
321     /**
322      * Returns the state of the button. True if the
323      * toggle button is selected, false if it's not.
324      * @return true if the toggle button is selected, otherwise false
325      */
326     public boolean isSelected() {
327         return model.isSelected();
328     }
329 
330     /**
331      * Sets the state of the button. Note that this method does not
332      * trigger an <code>actionEvent</code>.
333      * Call <code>doClick</code> to perform a programmatic action change.
334      *
335      * @param b  true if the button is selected, otherwise false
336      */
337     public void setSelected(boolean b) {
338         boolean oldValue = isSelected();
339 
340         // TIGER - 4840653
341         // Removed code which fired an AccessibleState.SELECTED
342         // PropertyChangeEvent since this resulted in two
343         // identical events being fired since
344         // AbstractButton.fireItemStateChanged also fires the
345         // same event. This caused screen readers to speak the
346         // name of the item twice.
347 
348         model.setSelected(b);
349     }
350 
351     /**
352      * Programmatically perform a "click". This does the same
353      * thing as if the user had pressed and released the button.
354      */
355     public void doClick() {
356         doClick(68);
357     }
358 
359     /**
360      * Programmatically perform a "click". This does the same
361      * thing as if the user had pressed and released the button.
362      * The button stays visually "pressed" for <code>pressTime</code>
363      *  milliseconds.
364      *
365      * @param pressTime the time to "hold down" the button, in milliseconds
366      */
367     public void doClick(int pressTime) {
368         Dimension size = getSize();
369         model.setArmed(true);
370         model.setPressed(true);
371         paintImmediately(new Rectangle(0,0, size.width, size.height));
372         try {
373             Thread.currentThread().sleep(pressTime);
374         } catch(InterruptedException ie) {
375         }
376         model.setPressed(false);
377         model.setArmed(false);
378     }
379 
380     /**
381      * Sets space for margin between the button's border and
382      * the label. Setting to <code>null</code> will cause the button to
383      * use the default margin.  The button's default <code>Border</code>
384      * object will use this value to create the proper margin.
385      * However, if a non-default border is set on the button,
386      * it is that <code>Border</code> object's responsibility to create the
387      * appropriate margin space (else this property will
388      * effectively be ignored).
389      *
390      * @param m the space between the border and the label
391      *
392      * @beaninfo
393      *        bound: true
394      *    attribute: visualUpdate true
395      *  description: The space between the button's border and the label.
396      */
397     public void setMargin(Insets m) {
398         // Cache the old margin if it comes from the UI
399         if(m instanceof UIResource) {
400             defaultMargin = m;
401         } else if(margin instanceof UIResource) {
402             defaultMargin = margin;
403         }
404 
405         // If the client passes in a null insets, restore the margin
406         // from the UI if possible
407         if(m == null && defaultMargin != null) {
408             m = defaultMargin;
409         }
410 
411         Insets old = margin;
412         margin = m;
413         firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
414         if (old == null || !old.equals(m)) {
415             revalidate();
416             repaint();
417         }
418     }
419 
420     /**
421      * Returns the margin between the button's border and
422      * the label.
423      *
424      * @return an <code>Insets</code> object specifying the margin
425      *          between the botton's border and the label
426      * @see #setMargin
427      */
428     public Insets getMargin() {
429         return (margin == null) ? null : (Insets) margin.clone();
430     }
431 
432     /**
433      * Returns the default icon.
434      * @return the default <code>Icon</code>
435      * @see #setIcon
436      */
437     public Icon getIcon() {
438         return defaultIcon;
439     }
440 
441     /**
442      * Sets the button's default icon. This icon is
443      * also used as the "pressed" and "disabled" icon if
444      * there is no explicitly set pressed icon.
445      *
446      * @param defaultIcon the icon used as the default image
447      * @see #getIcon
448      * @see #setPressedIcon
449      * @beaninfo
450      *           bound: true
451      *       attribute: visualUpdate true
452      *     description: The button's default icon
453      */
454     public void setIcon(Icon defaultIcon) {
455         Icon oldValue = this.defaultIcon;
456         this.defaultIcon = defaultIcon;
457 
458         /* If the default icon has really changed and we had
459          * generated the disabled icon for this component,
460          * (i.e. setDisabledIcon() was never called) then
461          * clear the disabledIcon field.
462          */
463         if (defaultIcon != oldValue && (disabledIcon instanceof UIResource)) {
464             disabledIcon = null;
465         }
466 
467         firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
468         if (accessibleContext != null) {
469             accessibleContext.firePropertyChange(
470                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
471                 oldValue, defaultIcon);
472         }
473         if (defaultIcon != oldValue) {
474             if (defaultIcon == null || oldValue == null ||
475                 defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
476                 defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
477                 revalidate();
478             }
479             repaint();
480         }
481     }
482 
483     /**
484      * Returns the pressed icon for the button.
485      * @return the <code>pressedIcon</code> property
486      * @see #setPressedIcon
487      */
488     public Icon getPressedIcon() {
489         return pressedIcon;
490     }
491 
492     /**
493      * Sets the pressed icon for the button.
494      * @param pressedIcon the icon used as the "pressed" image
495      * @see #getPressedIcon
496      * @beaninfo
497      *        bound: true
498      *    attribute: visualUpdate true
499      *  description: The pressed icon for the button.
500      */
501     public void setPressedIcon(Icon pressedIcon) {
502         Icon oldValue = this.pressedIcon;
503         this.pressedIcon = pressedIcon;
504         firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
505         if (accessibleContext != null) {
506             accessibleContext.firePropertyChange(
507                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
508                 oldValue, pressedIcon);
509         }
510         if (pressedIcon != oldValue) {
511             if (getModel().isPressed()) {
512                 repaint();
513             }
514         }
515     }
516 
517     /**
518      * Returns the selected icon for the button.
519      * @return the <code>selectedIcon</code> property
520      * @see #setSelectedIcon
521      */
522     public Icon getSelectedIcon() {
523         return selectedIcon;
524     }
525 
526     /**
527      * Sets the selected icon for the button.
528      * @param selectedIcon the icon used as the "selected" image
529      * @see #getSelectedIcon
530      * @beaninfo
531      *        bound: true
532      *    attribute: visualUpdate true
533      *  description: The selected icon for the button.
534      */
535     public void setSelectedIcon(Icon selectedIcon) {
536         Icon oldValue = this.selectedIcon;
537         this.selectedIcon = selectedIcon;
538 
539         /* If the default selected icon has really changed and we had
540          * generated the disabled selected icon for this component,
541          * (i.e. setDisabledSelectedIcon() was never called) then
542          * clear the disabledSelectedIcon field.
543          */
544         if (selectedIcon != oldValue &&
545             disabledSelectedIcon instanceof UIResource) {
546 
547             disabledSelectedIcon = null;
548         }
549 
550         firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
551         if (accessibleContext != null) {
552             accessibleContext.firePropertyChange(
553                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
554                 oldValue, selectedIcon);
555         }
556         if (selectedIcon != oldValue) {
557             if (isSelected()) {
558                 repaint();
559             }
560         }
561     }
562 
563     /**
564      * Returns the rollover icon for the button.
565      * @return the <code>rolloverIcon</code> property
566      * @see #setRolloverIcon
567      */
568     public Icon getRolloverIcon() {
569         return rolloverIcon;
570     }
571 
572     /**
573      * Sets the rollover icon for the button.
574      * @param rolloverIcon the icon used as the "rollover" image
575      * @see #getRolloverIcon
576      * @beaninfo
577      *        bound: true
578      *    attribute: visualUpdate true
579      *  description: The rollover icon for the button.
580      */
581     public void setRolloverIcon(Icon rolloverIcon) {
582         Icon oldValue = this.rolloverIcon;
583         this.rolloverIcon = rolloverIcon;
584         firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
585         if (accessibleContext != null) {
586             accessibleContext.firePropertyChange(
587                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
588                 oldValue, rolloverIcon);
589         }
590         setRolloverEnabled(true);
591         if (rolloverIcon != oldValue) {
592             // No way to determine whether we are currently in
593             // a rollover state, so repaint regardless
594             repaint();
595         }
596 
597     }
598 
599     /**
600      * Returns the rollover selection icon for the button.
601      * @return the <code>rolloverSelectedIcon</code> property
602      * @see #setRolloverSelectedIcon
603      */
604     public Icon getRolloverSelectedIcon() {
605         return rolloverSelectedIcon;
606     }
607 
608     /**
609      * Sets the rollover selected icon for the button.
610      * @param rolloverSelectedIcon the icon used as the
611      *          "selected rollover" image
612      * @see #getRolloverSelectedIcon
613      * @beaninfo
614      *        bound: true
615      *    attribute: visualUpdate true
616      *  description: The rollover selected icon for the button.
617      */
618     public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
619         Icon oldValue = this.rolloverSelectedIcon;
620         this.rolloverSelectedIcon = rolloverSelectedIcon;
621         firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
622         if (accessibleContext != null) {
623             accessibleContext.firePropertyChange(
624                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
625                 oldValue, rolloverSelectedIcon);
626         }
627         setRolloverEnabled(true);
628         if (rolloverSelectedIcon != oldValue) {
629             // No way to determine whether we are currently in
630             // a rollover state, so repaint regardless
631             if (isSelected()) {
632                 repaint();
633             }
634         }
635     }
636 
637     /**
638      * Returns the icon used by the button when it's disabled.
639      * If no disabled icon has been set this will forward the call to
640      * the look and feel to construct an appropriate disabled Icon.
641      * <p>
642      * Some look and feels might not render the disabled Icon, in which
643      * case they will ignore this.
644      *
645      * @return the <code>disabledIcon</code> property
646      * @see #getPressedIcon
647      * @see #setDisabledIcon
648      * @see javax.swing.LookAndFeel#getDisabledIcon
649      */
650     @Transient
651     public Icon getDisabledIcon() {
652         if (disabledIcon == null) {
653             disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
654             if (disabledIcon != null) {
655                 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);
656             }
657         }
658         return disabledIcon;
659     }
660 
661     /**
662      * Sets the disabled icon for the button.
663      * @param disabledIcon the icon used as the disabled image
664      * @see #getDisabledIcon
665      * @beaninfo
666      *        bound: true
667      *    attribute: visualUpdate true
668      *  description: The disabled icon for the button.
669      */
670     public void setDisabledIcon(Icon disabledIcon) {
671         Icon oldValue = this.disabledIcon;
672         this.disabledIcon = disabledIcon;
673         firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
674         if (accessibleContext != null) {
675             accessibleContext.firePropertyChange(
676                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
677                 oldValue, disabledIcon);
678         }
679         if (disabledIcon != oldValue) {
680             if (!isEnabled()) {
681                 repaint();
682             }
683         }
684     }
685 
686     /**
687      * Returns the icon used by the button when it's disabled and selected.
688      * If no disabled selection icon has been set, this will forward
689      * the call to the LookAndFeel to construct an appropriate disabled
690      * Icon from the selection icon if it has been set and to
691      * <code>getDisabledIcon()</code> otherwise.
692      * <p>
693      * Some look and feels might not render the disabled selected Icon, in
694      * which case they will ignore this.
695      *
696      * @return the <code>disabledSelectedIcon</code> property
697      * @see #getDisabledIcon
698      * @see #setDisabledSelectedIcon
699      * @see javax.swing.LookAndFeel#getDisabledSelectedIcon
700      */
701     public Icon getDisabledSelectedIcon() {
702         if (disabledSelectedIcon == null) {
703              if (selectedIcon != null) {
704                  disabledSelectedIcon = UIManager.getLookAndFeel().
705                          getDisabledSelectedIcon(this, getSelectedIcon());
706              } else {
707                  return getDisabledIcon();
708              }
709         }
710         return disabledSelectedIcon;
711     }
712 
713     /**
714      * Sets the disabled selection icon for the button.
715      * @param disabledSelectedIcon the icon used as the disabled
716      *          selection image
717      * @see #getDisabledSelectedIcon
718      * @beaninfo
719      *        bound: true
720      *    attribute: visualUpdate true
721      *  description: The disabled selection icon for the button.
722      */
723     public void setDisabledSelectedIcon(Icon disabledSelectedIcon) {
724         Icon oldValue = this.disabledSelectedIcon;
725         this.disabledSelectedIcon = disabledSelectedIcon;
726         firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, oldValue, disabledSelectedIcon);
727         if (accessibleContext != null) {
728             accessibleContext.firePropertyChange(
729                 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
730                 oldValue, disabledSelectedIcon);
731         }
732         if (disabledSelectedIcon != oldValue) {
733             if (disabledSelectedIcon == null || oldValue == null ||
734                 disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
735                 disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
736                 revalidate();
737             }
738             if (!isEnabled() && isSelected()) {
739                 repaint();
740             }
741         }
742     }
743 
744     /**
745      * Returns the vertical alignment of the text and icon.
746      *
747      * @return the <code>verticalAlignment</code> property, one of the
748      *          following values:
749      * <ul>
750      * <li>{@code SwingConstants.CENTER} (the default)
751      * <li>{@code SwingConstants.TOP}
752      * <li>{@code SwingConstants.BOTTOM}
753      * </ul>
754      */
755     public int getVerticalAlignment() {
756         return verticalAlignment;
757     }
758 
759     /**
760      * Sets the vertical alignment of the icon and text.
761      * @param alignment one of the following values:
762      * <ul>
763      * <li>{@code SwingConstants.CENTER} (the default)
764      * <li>{@code SwingConstants.TOP}
765      * <li>{@code SwingConstants.BOTTOM}
766      * </ul>
767      * @throws IllegalArgumentException if the alignment is not one of the legal
768      *         values listed above
769      * @beaninfo
770      *        bound: true
771      *         enum: TOP    SwingConstants.TOP
772      *               CENTER SwingConstants.CENTER
773      *               BOTTOM  SwingConstants.BOTTOM
774      *    attribute: visualUpdate true
775      *  description: The vertical alignment of the icon and text.
776      */
777     public void setVerticalAlignment(int alignment) {
778         if (alignment == verticalAlignment) return;
779         int oldValue = verticalAlignment;
780         verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
781         firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment);         repaint();
782     }
783 
784     /**
785      * Returns the horizontal alignment of the icon and text.
786      * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
787      * but subclasses such as {@code JCheckBox} may use a different default.
788      *
789      * @return the <code>horizontalAlignment</code> property,
790      *             one of the following values:
791      * <ul>
792      *   <li>{@code SwingConstants.RIGHT}
793      *   <li>{@code SwingConstants.LEFT}
794      *   <li>{@code SwingConstants.CENTER}
795      *   <li>{@code SwingConstants.LEADING}
796      *   <li>{@code SwingConstants.TRAILING}
797      * </ul>
798      */
799     public int getHorizontalAlignment() {
800         return horizontalAlignment;
801     }
802 
803     /**
804      * Sets the horizontal alignment of the icon and text.
805      * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
806      * but subclasses such as {@code JCheckBox} may use a different default.
807      *
808      * @param alignment the alignment value, one of the following values:
809      * <ul>
810      *   <li>{@code SwingConstants.RIGHT}
811      *   <li>{@code SwingConstants.LEFT}
812      *   <li>{@code SwingConstants.CENTER}
813      *   <li>{@code SwingConstants.LEADING}
814      *   <li>{@code SwingConstants.TRAILING}
815      * </ul>
816      * @throws IllegalArgumentException if the alignment is not one of the
817      *         valid values
818      * @beaninfo
819      *        bound: true
820      *         enum: LEFT     SwingConstants.LEFT
821      *               CENTER   SwingConstants.CENTER
822      *               RIGHT    SwingConstants.RIGHT
823      *               LEADING  SwingConstants.LEADING
824      *               TRAILING SwingConstants.TRAILING
825      *    attribute: visualUpdate true
826      *  description: The horizontal alignment of the icon and text.
827      */
828     public void setHorizontalAlignment(int alignment) {
829         if (alignment == horizontalAlignment) return;
830         int oldValue = horizontalAlignment;
831         horizontalAlignment = checkHorizontalKey(alignment,
832                                                  "horizontalAlignment");
833         firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
834                            oldValue, horizontalAlignment);
835         repaint();
836     }
837 
838 
839     /**
840      * Returns the vertical position of the text relative to the icon.
841      * @return the <code>verticalTextPosition</code> property,
842      *          one of the following values:
843      * <ul>
844      * <li>{@code SwingConstants.CENTER} (the default)
845      * <li>{@code SwingConstants.TOP}
846      * <li>{@code SwingConstants.BOTTOM}
847      * </ul>
848      */
849     public int getVerticalTextPosition() {
850         return verticalTextPosition;
851     }
852 
853     /**
854      * Sets the vertical position of the text relative to the icon.
855      * @param textPosition  one of the following values:
856      * <ul>
857      * <li>{@code SwingConstants.CENTER} (the default)
858      * <li>{@code SwingConstants.TOP}
859      * <li>{@code SwingConstants.BOTTOM}
860      * </ul>
861      * @beaninfo
862      *        bound: true
863      *         enum: TOP    SwingConstants.TOP
864      *               CENTER SwingConstants.CENTER
865      *               BOTTOM SwingConstants.BOTTOM
866      *    attribute: visualUpdate true
867      *  description: The vertical position of the text relative to the icon.
868      */
869     public void setVerticalTextPosition(int textPosition) {
870         if (textPosition == verticalTextPosition) return;
871         int oldValue = verticalTextPosition;
872         verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
873         firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
874         revalidate();
875         repaint();
876     }
877 
878     /**
879      * Returns the horizontal position of the text relative to the icon.
880      * @return the <code>horizontalTextPosition</code> property,
881      *          one of the following values:
882      * <ul>
883      * <li>{@code SwingConstants.RIGHT}
884      * <li>{@code SwingConstants.LEFT}
885      * <li>{@code SwingConstants.CENTER}
886      * <li>{@code SwingConstants.LEADING}
887      * <li>{@code SwingConstants.TRAILING} (the default)
888      * </ul>
889      */
890     public int getHorizontalTextPosition() {
891         return horizontalTextPosition;
892     }
893 
894     /**
895      * Sets the horizontal position of the text relative to the icon.
896      * @param textPosition one of the following values:
897      * <ul>
898      * <li>{@code SwingConstants.RIGHT}
899      * <li>{@code SwingConstants.LEFT}
900      * <li>{@code SwingConstants.CENTER}
901      * <li>{@code SwingConstants.LEADING}
902      * <li>{@code SwingConstants.TRAILING} (the default)
903      * </ul>
904      * @exception IllegalArgumentException if <code>textPosition</code>
905      *          is not one of the legal values listed above
906      * @beaninfo
907      *        bound: true
908      *         enum: LEFT     SwingConstants.LEFT
909      *               CENTER   SwingConstants.CENTER
910      *               RIGHT    SwingConstants.RIGHT
911      *               LEADING  SwingConstants.LEADING
912      *               TRAILING SwingConstants.TRAILING
913      *    attribute: visualUpdate true
914      *  description: The horizontal position of the text relative to the icon.
915      */
916     public void setHorizontalTextPosition(int textPosition) {
917         if (textPosition == horizontalTextPosition) return;
918         int oldValue = horizontalTextPosition;
919         horizontalTextPosition = checkHorizontalKey(textPosition,
920                                                     "horizontalTextPosition");
921         firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
922                            oldValue,
923                            horizontalTextPosition);
924         revalidate();
925         repaint();
926     }
927 
928     /**
929      * Returns the amount of space between the text and the icon
930      * displayed in this button.
931      *
932      * @return an int equal to the number of pixels between the text
933      *         and the icon.
934      * @since 1.4
935      * @see #setIconTextGap
936      */
937     public int getIconTextGap() {
938         return iconTextGap;
939     }
940 
941     /**
942      * If both the icon and text properties are set, this property
943      * defines the space between them.
944      * <p>
945      * The default value of this property is 4 pixels.
946      * <p>
947      * This is a JavaBeans bound property.
948      *
949      * @since 1.4
950      * @see #getIconTextGap
951      * @beaninfo
952      *        bound: true
953      *    attribute: visualUpdate true
954      *  description: If both the icon and text properties are set, this
955      *               property defines the space between them.
956      */
957     public void setIconTextGap(int iconTextGap) {
958         int oldValue = this.iconTextGap;
959         this.iconTextGap = iconTextGap;
960         iconTextGapSet = true;
961         firePropertyChange("iconTextGap", oldValue, iconTextGap);
962         if (iconTextGap != oldValue) {
963             revalidate();
964             repaint();
965         }
966     }
967 
968     /**
969      * Verify that the {@code key} argument is a legal value for the
970      * {@code horizontalAlignment} and {@code horizontalTextPosition}
971      * properties. Valid values are:
972      * <ul>
973      *   <li>{@code SwingConstants.RIGHT}
974      *   <li>{@code SwingConstants.LEFT}
975      *   <li>{@code SwingConstants.CENTER}
976      *   <li>{@code SwingConstants.LEADING}
977      *   <li>{@code SwingConstants.TRAILING}
978      * </ul>
979      *
980      * @param key the property value to check
981      * @param exception the message to use in the
982      *        {@code IllegalArgumentException} that is thrown for an invalid
983      *        value
984      * @return the {@code key} argument
985      * @exception IllegalArgumentException if key is not one of the legal
986      *            values listed above
987      * @see #setHorizontalTextPosition
988      * @see #setHorizontalAlignment
989      */
990     protected int checkHorizontalKey(int key, String exception) {
991         if ((key == LEFT) ||
992             (key == CENTER) ||
993             (key == RIGHT) ||
994             (key == LEADING) ||
995             (key == TRAILING)) {
996             return key;
997         } else {
998             throw new IllegalArgumentException(exception);
999         }
1000     }
1001 
1002     /**
1003      * Verify that the {@code key} argument is a legal value for the
1004      * vertical properties. Valid values are:
1005      * <ul>
1006      *   <li>{@code SwingConstants.CENTER}
1007      *   <li>{@code SwingConstants.TOP}
1008      *   <li>{@code SwingConstants.BOTTOM}
1009      * </ul>
1010      *
1011      * @param key the property value to check
1012      * @param exception the message to use in the
1013      *        {@code IllegalArgumentException} that is thrown for an invalid
1014      *        value
1015      * @return the {@code key} argument
1016      * @exception IllegalArgumentException if key is not one of the legal
1017      *            values listed above
1018      */
1019     protected int checkVerticalKey(int key, String exception) {
1020         if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {
1021             return key;
1022         } else {
1023             throw new IllegalArgumentException(exception);
1024         }
1025     }
1026 
1027     /**
1028      *{@inheritDoc}
1029      *
1030      * @since 1.6
1031      */
1032     public void removeNotify() {
1033         super.removeNotify();
1034         if(isRolloverEnabled()) {
1035             getModel().setRollover(false);
1036         }
1037     }
1038 
1039     /**
1040      * Sets the action command for this button.
1041      * @param actionCommand the action command for this button
1042      */
1043     public void setActionCommand(String actionCommand) {
1044         getModel().setActionCommand(actionCommand);
1045     }
1046 
1047     /**
1048      * Returns the action command for this button.
1049      * @return the action command for this button
1050      */
1051     public String getActionCommand() {
1052         String ac = getModel().getActionCommand();
1053         if(ac == null) {
1054             ac = getText();
1055         }
1056         return ac;
1057     }
1058 
1059     private Action action;
1060     private PropertyChangeListener actionPropertyChangeListener;
1061 
1062     /**
1063      * Sets the <code>Action</code>.
1064      * The new <code>Action</code> replaces any previously set
1065      * <code>Action</code> but does not affect <code>ActionListeners</code>
1066      * independently added with <code>addActionListener</code>.
1067      * If the <code>Action</code> is already a registered
1068      * <code>ActionListener</code> for the button, it is not re-registered.
1069      * <p>
1070      * Setting the <code>Action</code> results in immediately changing
1071      * all the properties described in <a href="Action.html#buttonActions">
1072      * Swing Components Supporting <code>Action</code></a>.
1073      * Subsequently, the button's properties are automatically updated
1074      * as the <code>Action</code>'s properties change.
1075      * <p>
1076      * This method uses three other methods to set
1077      * and help track the <code>Action</code>'s property values.
1078      * It uses the <code>configurePropertiesFromAction</code> method
1079      * to immediately change the button's properties.
1080      * To track changes in the <code>Action</code>'s property values,
1081      * this method registers the <code>PropertyChangeListener</code>
1082      * returned by <code>createActionPropertyChangeListener</code>. The
1083      * default {@code PropertyChangeListener} invokes the
1084      * {@code actionPropertyChanged} method when a property in the
1085      * {@code Action} changes.
1086      *
1087      * @param a the <code>Action</code> for the <code>AbstractButton</code>,
1088      *          or <code>null</code>
1089      * @since 1.3
1090      * @see Action
1091      * @see #getAction
1092      * @see #configurePropertiesFromAction
1093      * @see #createActionPropertyChangeListener
1094      * @see #actionPropertyChanged
1095      * @beaninfo
1096      *        bound: true
1097      *    attribute: visualUpdate true
1098      *  description: the Action instance connected with this ActionEvent source
1099      */
1100     public void setAction(Action a) {
1101         Action oldValue = getAction();
1102         if (action==null || !action.equals(a)) {
1103             action = a;
1104             if (oldValue!=null) {
1105                 removeActionListener(oldValue);
1106                 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1107                 actionPropertyChangeListener = null;
1108             }
1109             configurePropertiesFromAction(action);
1110             if (action!=null) {
1111                 // Don't add if it is already a listener
1112                 if (!isListener(ActionListener.class, action)) {
1113                     addActionListener(action);
1114                 }
1115                 // Reverse linkage:
1116                 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1117                 action.addPropertyChangeListener(actionPropertyChangeListener);
1118             }
1119             firePropertyChange("action", oldValue, action);
1120         }
1121     }
1122 
1123     private boolean isListener(Class c, ActionListener a) {
1124         boolean isListener = false;
1125         Object[] listeners = listenerList.getListenerList();
1126         for (int i = listeners.length-2; i>=0; i-=2) {
1127             if (listeners[i]==c && listeners[i+1]==a) {
1128                     isListener=true;
1129             }
1130         }
1131         return isListener;
1132     }
1133 
1134     /**
1135      * Returns the currently set <code>Action</code> for this
1136      * <code>ActionEvent</code> source, or <code>null</code>
1137      * if no <code>Action</code> is set.
1138      *
1139      * @return the <code>Action</code> for this <code>ActionEvent</code>
1140      *          source, or <code>null</code>
1141      * @since 1.3
1142      * @see Action
1143      * @see #setAction
1144      */
1145     public Action getAction() {
1146         return action;
1147     }
1148 
1149     /**
1150      * Sets the properties on this button to match those in the specified
1151      * <code>Action</code>.  Refer to <a href="Action.html#buttonActions">
1152      * Swing Components Supporting <code>Action</code></a> for more
1153      * details as to which properties this sets.
1154      *
1155      * @param a the <code>Action</code> from which to get the properties,
1156      *          or <code>null</code>
1157      * @since 1.3
1158      * @see Action
1159      * @see #setAction
1160      */
1161     protected void configurePropertiesFromAction(Action a) {
1162         setMnemonicFromAction(a);
1163         setTextFromAction(a, false);
1164         AbstractAction.setToolTipTextFromAction(this, a);
1165         setIconFromAction(a);
1166         setActionCommandFromAction(a);
1167         AbstractAction.setEnabledFromAction(this, a);
1168         if (AbstractAction.hasSelectedKey(a) &&
1169                 shouldUpdateSelectedStateFromAction()) {
1170             setSelectedFromAction(a);
1171         }
1172         setDisplayedMnemonicIndexFromAction(a, false);
1173     }
1174 
1175     void clientPropertyChanged(Object key, Object oldValue,
1176                                Object newValue) {
1177         if (key == "hideActionText") {
1178             boolean current = (newValue instanceof Boolean) ?
1179                                 (Boolean)newValue : false;
1180             if (getHideActionText() != current) {
1181                 setHideActionText(current);
1182             }
1183         }
1184     }
1185 
1186     /**
1187      * Button subclasses that support mirroring the selected state from
1188      * the action should override this to return true.  AbstractButton's
1189      * implementation returns false.
1190      */
1191     boolean shouldUpdateSelectedStateFromAction() {
1192         return false;
1193     }
1194 
1195     /**
1196      * Updates the button's state in response to property changes in the
1197      * associated action. This method is invoked from the
1198      * {@code PropertyChangeListener} returned from
1199      * {@code createActionPropertyChangeListener}. Subclasses do not normally
1200      * need to invoke this. Subclasses that support additional {@code Action}
1201      * properties should override this and
1202      * {@code configurePropertiesFromAction}.
1203      * <p>
1204      * Refer to the table at <a href="Action.html#buttonActions">
1205      * Swing Components Supporting <code>Action</code></a> for a list of
1206      * the properties this method sets.
1207      *
1208      * @param action the <code>Action</code> associated with this button
1209      * @param propertyName the name of the property that changed
1210      * @since 1.6
1211      * @see Action
1212      * @see #configurePropertiesFromAction
1213      */
1214     protected void actionPropertyChanged(Action action, String propertyName) {
1215         if (propertyName == Action.NAME) {
1216             setTextFromAction(action, true);
1217         } else if (propertyName == "enabled") {
1218             AbstractAction.setEnabledFromAction(this, action);
1219         } else if (propertyName == Action.SHORT_DESCRIPTION) {
1220             AbstractAction.setToolTipTextFromAction(this, action);
1221         } else if (propertyName == Action.SMALL_ICON) {
1222             smallIconChanged(action);
1223         } else if (propertyName == Action.MNEMONIC_KEY) {
1224             setMnemonicFromAction(action);
1225         } else if (propertyName == Action.ACTION_COMMAND_KEY) {
1226             setActionCommandFromAction(action);
1227         } else if (propertyName == Action.SELECTED_KEY &&
1228                    AbstractAction.hasSelectedKey(action) &&
1229                    shouldUpdateSelectedStateFromAction()) {
1230             setSelectedFromAction(action);
1231         } else if (propertyName == Action.DISPLAYED_MNEMONIC_INDEX_KEY) {
1232             setDisplayedMnemonicIndexFromAction(action, true);
1233         } else if (propertyName == Action.LARGE_ICON_KEY) {
1234             largeIconChanged(action);
1235         }
1236     }
1237 
1238     private void setDisplayedMnemonicIndexFromAction(
1239             Action a, boolean fromPropertyChange) {
1240         Integer iValue = (a == null) ? null :
1241                 (Integer)a.getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY);
1242         if (fromPropertyChange || iValue != null) {
1243             int value;
1244             if (iValue == null) {
1245                 value = -1;
1246             } else {
1247                 value = iValue;
1248                 String text = getText();
1249                 if (text == null || value >= text.length()) {
1250                     value = -1;
1251                 }
1252             }
1253             setDisplayedMnemonicIndex(value);
1254         }
1255     }
1256 
1257     private void setMnemonicFromAction(Action a) {
1258         Integer n = (a == null) ? null :
1259                                   (Integer)a.getValue(Action.MNEMONIC_KEY);
1260         setMnemonic((n == null) ? '\0' : n);
1261     }
1262 
1263     private void setTextFromAction(Action a, boolean propertyChange) {
1264         boolean hideText = getHideActionText();
1265         if (!propertyChange) {
1266             setText((a != null && !hideText) ?
1267                         (String)a.getValue(Action.NAME) : null);
1268         }
1269         else if (!hideText) {
1270             setText((String)a.getValue(Action.NAME));
1271         }
1272     }
1273 
1274     void setIconFromAction(Action a) {
1275         Icon icon = null;
1276         if (a != null) {
1277             icon = (Icon)a.getValue(Action.LARGE_ICON_KEY);
1278             if (icon == null) {
1279                 icon = (Icon)a.getValue(Action.SMALL_ICON);
1280             }
1281         }
1282         setIcon(icon);
1283     }
1284 
1285     void smallIconChanged(Action a) {
1286         if (a.getValue(Action.LARGE_ICON_KEY) == null) {
1287             setIconFromAction(a);
1288         }
1289     }
1290 
1291     void largeIconChanged(Action a) {
1292         setIconFromAction(a);
1293     }
1294 
1295     private void setActionCommandFromAction(Action a) {
1296         setActionCommand((a != null) ?
1297                              (String)a.getValue(Action.ACTION_COMMAND_KEY) :
1298                              null);
1299     }
1300 
1301     /**
1302      * Sets the seleted state of the button from the action.  This is defined
1303      * here, but not wired up.  Subclasses like JToggleButton and
1304      * JCheckBoxMenuItem make use of it.
1305      *
1306      * @param a the Action
1307      */
1308     private void setSelectedFromAction(Action a) {
1309         boolean selected = false;
1310         if (a != null) {
1311             selected = AbstractAction.isSelected(a);
1312         }
1313         if (selected != isSelected()) {
1314             // This won't notify ActionListeners, but that should be
1315             // ok as the change is coming from the Action.
1316             setSelected(selected);
1317             // Make sure the change actually took effect
1318             if (!selected && isSelected()) {
1319                 if (getModel() instanceof DefaultButtonModel) {
1320                     ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup();
1321                     if (group != null) {
1322                         group.clearSelection();
1323                     }
1324                 }
1325             }
1326         }
1327     }
1328 
1329     /**
1330      * Creates and returns a <code>PropertyChangeListener</code> that is
1331      * responsible for listening for changes from the specified
1332      * <code>Action</code> and updating the appropriate properties.
1333      * <p>
1334      * <b>Warning:</b> If you subclass this do not create an anonymous
1335      * inner class.  If you do the lifetime of the button will be tied to
1336      * that of the <code>Action</code>.
1337      *
1338      * @param a the button's action
1339      * @since 1.3
1340      * @see Action
1341      * @see #setAction
1342      */
1343     protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1344         return createActionPropertyChangeListener0(a);
1345     }
1346 
1347 
1348     PropertyChangeListener createActionPropertyChangeListener0(Action a) {
1349         return new ButtonActionPropertyChangeListener(this, a);
1350     }
1351 
1352     @SuppressWarnings("serial")
1353     private static class ButtonActionPropertyChangeListener
1354                  extends ActionPropertyChangeListener<AbstractButton> {
1355         ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
1356             super(b, a);
1357         }
1358         protected void actionPropertyChanged(AbstractButton button,
1359                                              Action action,
1360                                              PropertyChangeEvent e) {
1361             if (AbstractAction.shouldReconfigure(e)) {
1362                 button.configurePropertiesFromAction(action);
1363             } else {
1364                 button.actionPropertyChanged(action, e.getPropertyName());
1365             }
1366         }
1367     }
1368 
1369     /**
1370      * Gets the <code>borderPainted</code> property.
1371      *
1372      * @return the value of the <code>borderPainted</code> property
1373      * @see #setBorderPainted
1374      */
1375     public boolean isBorderPainted() {
1376         return paintBorder;
1377     }
1378 
1379     /**
1380      * Sets the <code>borderPainted</code> property.
1381      * If <code>true</code> and the button has a border,
1382      * the border is painted. The default value for the
1383      * <code>borderPainted</code> property is <code>true</code>.
1384      * <p>
1385      * Some look and feels might not support
1386      * the <code>borderPainted</code> property,
1387      * in which case they ignore this.
1388      *
1389      * @param b if true and border property is not <code>null</code>,
1390      *          the border is painted
1391      * @see #isBorderPainted
1392      * @beaninfo
1393      *        bound: true
1394      *    attribute: visualUpdate true
1395      *  description: Whether the border should be painted.
1396      */
1397     public void setBorderPainted(boolean b) {
1398         boolean oldValue = paintBorder;
1399         paintBorder = b;
1400         borderPaintedSet = true;
1401         firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
1402         if (b != oldValue) {
1403             revalidate();
1404             repaint();
1405         }
1406     }
1407 
1408     /**
1409      * Paint the button's border if <code>BorderPainted</code>
1410      * property is true and the button has a border.
1411      * @param g the <code>Graphics</code> context in which to paint
1412      *
1413      * @see #paint
1414      * @see #setBorder
1415      */
1416     protected void paintBorder(Graphics g) {
1417         if (isBorderPainted()) {
1418             super.paintBorder(g);
1419         }
1420     }
1421 
1422     /**
1423      * Gets the <code>paintFocus</code> property.
1424      *
1425      * @return the <code>paintFocus</code> property
1426      * @see #setFocusPainted
1427      */
1428     public boolean isFocusPainted() {
1429         return paintFocus;
1430     }
1431 
1432     /**
1433      * Sets the <code>paintFocus</code> property, which must
1434      * be <code>true</code> for the focus state to be painted.
1435      * The default value for the <code>paintFocus</code> property
1436      * is <code>true</code>.
1437      * Some look and feels might not paint focus state;
1438      * they will ignore this property.
1439      *
1440      * @param b if <code>true</code>, the focus state should be painted
1441      * @see #isFocusPainted
1442      * @beaninfo
1443      *        bound: true
1444      *    attribute: visualUpdate true
1445      *  description: Whether focus should be painted
1446      */
1447     public void setFocusPainted(boolean b) {
1448         boolean oldValue = paintFocus;
1449         paintFocus = b;
1450         firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
1451         if (b != oldValue && isFocusOwner()) {
1452             revalidate();
1453             repaint();
1454         }
1455     }
1456 
1457     /**
1458      * Gets the <code>contentAreaFilled</code> property.
1459      *
1460      * @return the <code>contentAreaFilled</code> property
1461      * @see #setContentAreaFilled
1462      */
1463     public boolean isContentAreaFilled() {
1464         return contentAreaFilled;
1465     }
1466 
1467     /**
1468      * Sets the <code>contentAreaFilled</code> property.
1469      * If <code>true</code> the button will paint the content
1470      * area.  If you wish to have a transparent button, such as
1471      * an icon only button, for example, then you should set
1472      * this to <code>false</code>. Do not call <code>setOpaque(false)</code>.
1473      * The default value for the the <code>contentAreaFilled</code>
1474      * property is <code>true</code>.
1475      * <p>
1476      * This function may cause the component's opaque property to change.
1477      * <p>
1478      * The exact behavior of calling this function varies on a
1479      * component-by-component and L&amp;F-by-L&amp;F basis.
1480      *
1481      * @param b if true, the content should be filled; if false
1482      *          the content area is not filled
1483      * @see #isContentAreaFilled
1484      * @see #setOpaque
1485      * @beaninfo
1486      *        bound: true
1487      *    attribute: visualUpdate true
1488      *  description: Whether the button should paint the content area
1489      *               or leave it transparent.
1490      */
1491     public void setContentAreaFilled(boolean b) {
1492         boolean oldValue = contentAreaFilled;
1493         contentAreaFilled = b;
1494         contentAreaFilledSet = true;
1495         firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
1496         if (b != oldValue) {
1497             repaint();
1498         }
1499     }
1500 
1501     /**
1502      * Gets the <code>rolloverEnabled</code> property.
1503      *
1504      * @return the value of the <code>rolloverEnabled</code> property
1505      * @see #setRolloverEnabled
1506      */
1507     public boolean isRolloverEnabled() {
1508         return rolloverEnabled;
1509     }
1510 
1511     /**
1512      * Sets the <code>rolloverEnabled</code> property, which
1513      * must be <code>true</code> for rollover effects to occur.
1514      * The default value for the <code>rolloverEnabled</code>
1515      * property is <code>false</code>.
1516      * Some look and feels might not implement rollover effects;
1517      * they will ignore this property.
1518      *
1519      * @param b if <code>true</code>, rollover effects should be painted
1520      * @see #isRolloverEnabled
1521      * @beaninfo
1522      *        bound: true
1523      *    attribute: visualUpdate true
1524      *  description: Whether rollover effects should be enabled.
1525      */
1526     public void setRolloverEnabled(boolean b) {
1527         boolean oldValue = rolloverEnabled;
1528         rolloverEnabled = b;
1529         rolloverEnabledSet = true;
1530         firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
1531         if (b != oldValue) {
1532             repaint();
1533         }
1534     }
1535 
1536     /**
1537      * Returns the keyboard mnemonic from the the current model.
1538      * @return the keyboard mnemonic from the model
1539      */
1540     public int getMnemonic() {
1541         return mnemonic;
1542     }
1543 
1544     /**
1545      * Sets the keyboard mnemonic on the current model.
1546      * The mnemonic is the key which when combined with the look and feel's
1547      * mouseless modifier (usually Alt) will activate this button
1548      * if focus is contained somewhere within this button's ancestor
1549      * window.
1550      * <p>
1551      * A mnemonic must correspond to a single key on the keyboard
1552      * and should be specified using one of the <code>VK_XXX</code>
1553      * keycodes defined in <code>java.awt.event.KeyEvent</code>.
1554      * These codes and the wider array of codes for international
1555      * keyboards may be obtained through
1556      * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>.
1557      * Mnemonics are case-insensitive, therefore a key event
1558      * with the corresponding keycode would cause the button to be
1559      * activated whether or not the Shift modifier was pressed.
1560      * <p>
1561      * If the character defined by the mnemonic is found within
1562      * the button's label string, the first occurrence of it
1563      * will be underlined to indicate the mnemonic to the user.
1564      *
1565      * @param mnemonic the key code which represents the mnemonic
1566      * @see     java.awt.event.KeyEvent
1567      * @see     #setDisplayedMnemonicIndex
1568      *
1569      * @beaninfo
1570      *        bound: true
1571      *    attribute: visualUpdate true
1572      *  description: the keyboard character mnemonic
1573      */
1574     public void setMnemonic(int mnemonic) {
1575         int oldValue = getMnemonic();
1576         model.setMnemonic(mnemonic);
1577         updateMnemonicProperties();
1578     }
1579 
1580     /**
1581      * This method is now obsolete, please use <code>setMnemonic(int)</code>
1582      * to set the mnemonic for a button.  This method is only designed
1583      * to handle character values which fall between 'a' and 'z' or
1584      * 'A' and 'Z'.
1585      *
1586      * @param mnemonic  a char specifying the mnemonic value
1587      * @see #setMnemonic(int)
1588      * @beaninfo
1589      *        bound: true
1590      *    attribute: visualUpdate true
1591      *  description: the keyboard character mnemonic
1592      */
1593     public void setMnemonic(char mnemonic) {
1594         int vk = (int) mnemonic;
1595         if(vk >= 'a' && vk <='z')
1596             vk -= ('a' - 'A');
1597         setMnemonic(vk);
1598     }
1599 
1600     /**
1601      * Provides a hint to the look and feel as to which character in the
1602      * text should be decorated to represent the mnemonic. Not all look and
1603      * feels may support this. A value of -1 indicates either there is no
1604      * mnemonic, the mnemonic character is not contained in the string, or
1605      * the developer does not wish the mnemonic to be displayed.
1606      * <p>
1607      * The value of this is updated as the properties relating to the
1608      * mnemonic change (such as the mnemonic itself, the text...).
1609      * You should only ever have to call this if
1610      * you do not wish the default character to be underlined. For example, if
1611      * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
1612      * to be decorated, as 'Save <u>A</u>s', you would have to invoke
1613      * <code>setDisplayedMnemonicIndex(5)</code> after invoking
1614      * <code>setMnemonic(KeyEvent.VK_A)</code>.
1615      *
1616      * @since 1.4
1617      * @param index Index into the String to underline
1618      * @exception IllegalArgumentException will be thrown if <code>index</code>
1619      *            is &gt;= length of the text, or &lt; -1
1620      * @see #getDisplayedMnemonicIndex
1621      *
1622      * @beaninfo
1623      *        bound: true
1624      *    attribute: visualUpdate true
1625      *  description: the index into the String to draw the keyboard character
1626      *               mnemonic at
1627      */
1628     public void setDisplayedMnemonicIndex(int index)
1629                                           throws IllegalArgumentException {
1630         int oldValue = mnemonicIndex;
1631         if (index == -1) {
1632             mnemonicIndex = -1;
1633         } else {
1634             String text = getText();
1635             int textLength = (text == null) ? 0 : text.length();
1636             if (index < -1 || index >= textLength) {  // index out of range
1637                 throw new IllegalArgumentException("index == " + index);
1638             }
1639         }
1640         mnemonicIndex = index;
1641         firePropertyChange("displayedMnemonicIndex", oldValue, index);
1642         if (index != oldValue) {
1643             revalidate();
1644             repaint();
1645         }
1646     }
1647 
1648     /**
1649      * Returns the character, as an index, that the look and feel should
1650      * provide decoration for as representing the mnemonic character.
1651      *
1652      * @since 1.4
1653      * @return index representing mnemonic character
1654      * @see #setDisplayedMnemonicIndex
1655      */
1656     public int getDisplayedMnemonicIndex() {
1657         return mnemonicIndex;
1658     }
1659 
1660     /**
1661      * Update the displayedMnemonicIndex property. This method
1662      * is called when either text or mnemonic changes. The new
1663      * value of the displayedMnemonicIndex property is the index
1664      * of the first occurrence of mnemonic in text.
1665      */
1666     private void updateDisplayedMnemonicIndex(String text, int mnemonic) {
1667         setDisplayedMnemonicIndex(
1668             SwingUtilities.findDisplayedMnemonicIndex(text, mnemonic));
1669     }
1670 
1671     /**
1672      * Brings the mnemonic property in accordance with model's mnemonic.
1673      * This is called when model's mnemonic changes. Also updates the
1674      * displayedMnemonicIndex property.
1675      */
1676     private void updateMnemonicProperties() {
1677         int newMnemonic = model.getMnemonic();
1678         if (mnemonic != newMnemonic) {
1679             int oldValue = mnemonic;
1680             mnemonic = newMnemonic;
1681             firePropertyChange(MNEMONIC_CHANGED_PROPERTY,
1682                                oldValue, mnemonic);
1683             updateDisplayedMnemonicIndex(getText(), mnemonic);
1684             revalidate();
1685             repaint();
1686         }
1687     }
1688 
1689     /**
1690      * Sets the amount of time (in milliseconds) required between
1691      * mouse press events for the button to generate the corresponding
1692      * action events.  After the initial mouse press occurs (and action
1693      * event generated) any subsequent mouse press events which occur
1694      * on intervals less than the threshhold will be ignored and no
1695      * corresponding action event generated.  By default the threshhold is 0,
1696      * which means that for each mouse press, an action event will be
1697      * fired, no matter how quickly the mouse clicks occur.  In buttons
1698      * where this behavior is not desirable (for example, the "OK" button
1699      * in a dialog), this threshhold should be set to an appropriate
1700      * positive value.
1701      *
1702      * @see #getMultiClickThreshhold
1703      * @param threshhold the amount of time required between mouse
1704      *        press events to generate corresponding action events
1705      * @exception   IllegalArgumentException if threshhold &lt; 0
1706      * @since 1.4
1707      */
1708     public void setMultiClickThreshhold(long threshhold) {
1709         if (threshhold < 0) {
1710             throw new IllegalArgumentException("threshhold must be >= 0");
1711         }
1712         this.multiClickThreshhold = threshhold;
1713     }
1714 
1715     /**
1716      * Gets the amount of time (in milliseconds) required between
1717      * mouse press events for the button to generate the corresponding
1718      * action events.
1719      *
1720      * @see #setMultiClickThreshhold
1721      * @return the amount of time required between mouse press events
1722      *         to generate corresponding action events
1723      * @since 1.4
1724      */
1725     public long getMultiClickThreshhold() {
1726         return multiClickThreshhold;
1727     }
1728 
1729     /**
1730      * Returns the model that this button represents.
1731      * @return the <code>model</code> property
1732      * @see #setModel
1733      */
1734     public ButtonModel getModel() {
1735         return model;
1736     }
1737 
1738     /**
1739      * Sets the model that this button represents.
1740      * @param newModel the new <code>ButtonModel</code>
1741      * @see #getModel
1742      * @beaninfo
1743      *        bound: true
1744      *  description: Model that the Button uses.
1745      */
1746     public void setModel(ButtonModel newModel) {
1747 
1748         ButtonModel oldModel = getModel();
1749 
1750         if (oldModel != null) {
1751             oldModel.removeChangeListener(changeListener);
1752             oldModel.removeActionListener(actionListener);
1753             oldModel.removeItemListener(itemListener);
1754             changeListener = null;
1755             actionListener = null;
1756             itemListener = null;
1757         }
1758 
1759         model = newModel;
1760 
1761         if (newModel != null) {
1762             changeListener = createChangeListener();
1763             actionListener = createActionListener();
1764             itemListener = createItemListener();
1765             newModel.addChangeListener(changeListener);
1766             newModel.addActionListener(actionListener);
1767             newModel.addItemListener(itemListener);
1768 
1769             updateMnemonicProperties();
1770             //We invoke setEnabled() from JComponent
1771             //because setModel() can be called from a constructor
1772             //when the button is not fully initialized
1773             super.setEnabled(newModel.isEnabled());
1774 
1775         } else {
1776             mnemonic = '\0';
1777         }
1778 
1779         updateDisplayedMnemonicIndex(getText(), mnemonic);
1780 
1781         firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
1782         if (newModel != oldModel) {
1783             revalidate();
1784             repaint();
1785         }
1786     }
1787 
1788 
1789     /**
1790      * Returns the L&amp;F object that renders this component.
1791      * @return the ButtonUI object
1792      * @see #setUI
1793      */
1794     public ButtonUI getUI() {
1795         return (ButtonUI) ui;
1796     }
1797 
1798 
1799     /**
1800      * Sets the L&amp;F object that renders this component.
1801      * @param ui the <code>ButtonUI</code> L&amp;F object
1802      * @see #getUI
1803      * @beaninfo
1804      *        bound: true
1805      *       hidden: true
1806      *    attribute: visualUpdate true
1807      *  description: The UI object that implements the LookAndFeel.
1808      */
1809     public void setUI(ButtonUI ui) {
1810         super.setUI(ui);
1811         // disabled icons are generated by the LF so they should be unset here
1812         if (disabledIcon instanceof UIResource) {
1813             setDisabledIcon(null);
1814         }
1815         if (disabledSelectedIcon instanceof UIResource) {
1816             setDisabledSelectedIcon(null);
1817         }
1818     }
1819 
1820 
1821     /**
1822      * Resets the UI property to a value from the current look
1823      * and feel.  Subtypes of <code>AbstractButton</code>
1824      * should override this to update the UI. For
1825      * example, <code>JButton</code> might do the following:
1826      * <pre>
1827      *      setUI((ButtonUI)UIManager.getUI(
1828      *          "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
1829      * </pre>
1830      */
1831     public void updateUI() {
1832     }
1833 
1834     /**
1835      * Adds the specified component to this container at the specified
1836      * index, refer to
1837      * {@link java.awt.Container#addImpl(Component, Object, int)}
1838      * for a complete description of this method.
1839      *
1840      * @param     comp the component to be added
1841      * @param     constraints an object expressing layout constraints
1842      *                 for this component
1843      * @param     index the position in the container's list at which to
1844      *                 insert the component, where <code>-1</code>
1845      *                 means append to the end
1846      * @exception IllegalArgumentException if <code>index</code> is invalid
1847      * @exception IllegalArgumentException if adding the container's parent
1848      *                  to itself
1849      * @exception IllegalArgumentException if adding a window to a container
1850      * @since 1.5
1851      */
1852     protected void addImpl(Component comp, Object constraints, int index) {
1853         if (!setLayout) {
1854             setLayout(new OverlayLayout(this));
1855         }
1856         super.addImpl(comp, constraints, index);
1857     }
1858 
1859     /**
1860      * Sets the layout manager for this container, refer to
1861      * {@link java.awt.Container#setLayout(LayoutManager)}
1862      * for a complete description of this method.
1863      *
1864      * @param mgr the specified layout manager
1865      * @since 1.5
1866      */
1867     public void setLayout(LayoutManager mgr) {
1868         setLayout = true;
1869         super.setLayout(mgr);
1870     }
1871 
1872     /**
1873      * Adds a <code>ChangeListener</code> to the button.
1874      * @param l the listener to be added
1875      */
1876     public void addChangeListener(ChangeListener l) {
1877         listenerList.add(ChangeListener.class, l);
1878     }
1879 
1880     /**
1881      * Removes a ChangeListener from the button.
1882      * @param l the listener to be removed
1883      */
1884     public void removeChangeListener(ChangeListener l) {
1885         listenerList.remove(ChangeListener.class, l);
1886     }
1887 
1888     /**
1889      * Returns an array of all the <code>ChangeListener</code>s added
1890      * to this AbstractButton with addChangeListener().
1891      *
1892      * @return all of the <code>ChangeListener</code>s added or an empty
1893      *         array if no listeners have been added
1894      * @since 1.4
1895      */
1896     public ChangeListener[] getChangeListeners() {
1897         return listenerList.getListeners(ChangeListener.class);
1898     }
1899 
1900     /**
1901      * Notifies all listeners that have registered interest for
1902      * notification on this event type.  The event instance
1903      * is lazily created.
1904      * @see EventListenerList
1905      */
1906     protected void fireStateChanged() {
1907         // Guaranteed to return a non-null array
1908         Object[] listeners = listenerList.getListenerList();
1909         // Process the listeners last to first, notifying
1910         // those that are interested in this event
1911         for (int i = listeners.length-2; i>=0; i-=2) {
1912             if (listeners[i]==ChangeListener.class) {
1913                 // Lazily create the event:
1914                 if (changeEvent == null)
1915                     changeEvent = new ChangeEvent(this);
1916                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
1917             }
1918         }
1919     }
1920 
1921     /**
1922      * Adds an <code>ActionListener</code> to the button.
1923      * @param l the <code>ActionListener</code> to be added
1924      */
1925     public void addActionListener(ActionListener l) {
1926         listenerList.add(ActionListener.class, l);
1927     }
1928 
1929     /**
1930      * Removes an <code>ActionListener</code> from the button.
1931      * If the listener is the currently set <code>Action</code>
1932      * for the button, then the <code>Action</code>
1933      * is set to <code>null</code>.
1934      *
1935      * @param l the listener to be removed
1936      */
1937     public void removeActionListener(ActionListener l) {
1938         if ((l != null) && (getAction() == l)) {
1939             setAction(null);
1940         } else {
1941             listenerList.remove(ActionListener.class, l);
1942         }
1943     }
1944 
1945     /**
1946      * Returns an array of all the <code>ActionListener</code>s added
1947      * to this AbstractButton with addActionListener().
1948      *
1949      * @return all of the <code>ActionListener</code>s added or an empty
1950      *         array if no listeners have been added
1951      * @since 1.4
1952      */
1953     public ActionListener[] getActionListeners() {
1954         return listenerList.getListeners(ActionListener.class);
1955     }
1956 
1957     /**
1958      * Subclasses that want to handle <code>ChangeEvents</code> differently
1959      * can override this to return another <code>ChangeListener</code>
1960      * implementation.
1961      *
1962      * @return the new <code>ChangeListener</code>
1963      */
1964     protected ChangeListener createChangeListener() {
1965         return getHandler();
1966     }
1967 
1968     /**
1969      * Extends <code>ChangeListener</code> to be serializable.
1970      * <p>
1971      * <strong>Warning:</strong>
1972      * Serialized objects of this class will not be compatible with
1973      * future Swing releases. The current serialization support is
1974      * appropriate for short term storage or RMI between applications running
1975      * the same version of Swing.  As of 1.4, support for long term storage
1976      * of all JavaBeans&trade;
1977      * has been added to the <code>java.beans</code> package.
1978      * Please see {@link java.beans.XMLEncoder}.
1979      */
1980     @SuppressWarnings("serial")
1981     protected class ButtonChangeListener implements ChangeListener, Serializable {
1982         // NOTE: This class is NOT used, instead the functionality has
1983         // been moved to Handler.
1984         ButtonChangeListener() {
1985         }
1986 
1987         public void stateChanged(ChangeEvent e) {
1988             getHandler().stateChanged(e);
1989         }
1990     }
1991 
1992 
1993     /**
1994      * Notifies all listeners that have registered interest for
1995      * notification on this event type.  The event instance
1996      * is lazily created using the <code>event</code>
1997      * parameter.
1998      *
1999      * @param event  the <code>ActionEvent</code> object
2000      * @see EventListenerList
2001      */
2002     protected void fireActionPerformed(ActionEvent event) {
2003         // Guaranteed to return a non-null array
2004         Object[] listeners = listenerList.getListenerList();
2005         ActionEvent e = null;
2006         // Process the listeners last to first, notifying
2007         // those that are interested in this event
2008         for (int i = listeners.length-2; i>=0; i-=2) {
2009             if (listeners[i]==ActionListener.class) {
2010                 // Lazily create the event:
2011                 if (e == null) {
2012                       String actionCommand = event.getActionCommand();
2013                       if(actionCommand == null) {
2014                          actionCommand = getActionCommand();
2015                       }
2016                       e = new ActionEvent(AbstractButton.this,
2017                                           ActionEvent.ACTION_PERFORMED,
2018                                           actionCommand,
2019                                           event.getWhen(),
2020                                           event.getModifiers());
2021                 }
2022                 ((ActionListener)listeners[i+1]).actionPerformed(e);
2023             }
2024         }
2025     }
2026 
2027     /**
2028      * Notifies all listeners that have registered interest for
2029      * notification on this event type.  The event instance
2030      * is lazily created using the <code>event</code> parameter.
2031      *
2032      * @param event  the <code>ItemEvent</code> object
2033      * @see EventListenerList
2034      */
2035     protected void fireItemStateChanged(ItemEvent event) {
2036         // Guaranteed to return a non-null array
2037         Object[] listeners = listenerList.getListenerList();
2038         ItemEvent e = null;
2039         // Process the listeners last to first, notifying
2040         // those that are interested in this event
2041         for (int i = listeners.length-2; i>=0; i-=2) {
2042             if (listeners[i]==ItemListener.class) {
2043                 // Lazily create the event:
2044                 if (e == null) {
2045                     e = new ItemEvent(AbstractButton.this,
2046                                       ItemEvent.ITEM_STATE_CHANGED,
2047                                       AbstractButton.this,
2048                                       event.getStateChange());
2049                 }
2050                 ((ItemListener)listeners[i+1]).itemStateChanged(e);
2051             }
2052         }
2053         if (accessibleContext != null) {
2054             if (event.getStateChange() == ItemEvent.SELECTED) {
2055                 accessibleContext.firePropertyChange(
2056                     AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2057                     null, AccessibleState.SELECTED);
2058                 accessibleContext.firePropertyChange(
2059                     AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
2060                     Integer.valueOf(0), Integer.valueOf(1));
2061             } else {
2062                 accessibleContext.firePropertyChange(
2063                     AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2064                     AccessibleState.SELECTED, null);
2065                 accessibleContext.firePropertyChange(
2066                     AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
2067                     Integer.valueOf(1), Integer.valueOf(0));
2068             }
2069         }
2070     }
2071 
2072 
2073     protected ActionListener createActionListener() {
2074         return getHandler();
2075     }
2076 
2077 
2078     protected ItemListener createItemListener() {
2079         return getHandler();
2080     }
2081 
2082 
2083     /**
2084      * Enables (or disables) the button.
2085      * @param b  true to enable the button, otherwise false
2086      */
2087     public void setEnabled(boolean b) {
2088         if (!b && model.isRollover()) {
2089             model.setRollover(false);
2090         }
2091         super.setEnabled(b);
2092         model.setEnabled(b);
2093     }
2094 
2095     // *** Deprecated java.awt.Button APIs below *** //
2096 
2097     /**
2098      * Returns the label text.
2099      *
2100      * @return a <code>String</code> containing the label
2101      * @deprecated - Replaced by <code>getText</code>
2102      */
2103     @Deprecated
2104     public String getLabel() {
2105         return getText();
2106     }
2107 
2108     /**
2109      * Sets the label text.
2110      *
2111      * @param label  a <code>String</code> containing the text
2112      * @deprecated - Replaced by <code>setText(text)</code>
2113      * @beaninfo
2114      *        bound: true
2115      *  description: Replace by setText(text)
2116      */
2117     @Deprecated
2118     public void setLabel(String label) {
2119         setText(label);
2120     }
2121 
2122     /**
2123      * Adds an <code>ItemListener</code> to the <code>checkbox</code>.
2124      * @param l  the <code>ItemListener</code> to be added
2125      */
2126     public void addItemListener(ItemListener l) {
2127         listenerList.add(ItemListener.class, l);
2128     }
2129 
2130     /**
2131      * Removes an <code>ItemListener</code> from the button.
2132      * @param l the <code>ItemListener</code> to be removed
2133      */
2134     public void removeItemListener(ItemListener l) {
2135         listenerList.remove(ItemListener.class, l);
2136     }
2137 
2138     /**
2139      * Returns an array of all the <code>ItemListener</code>s added
2140      * to this AbstractButton with addItemListener().
2141      *
2142      * @return all of the <code>ItemListener</code>s added or an empty
2143      *         array if no listeners have been added
2144      * @since 1.4
2145      */
2146     public ItemListener[] getItemListeners() {
2147         return listenerList.getListeners(ItemListener.class);
2148     }
2149 
2150    /**
2151      * Returns an array (length 1) containing the label or
2152      * <code>null</code> if the button is not selected.
2153      *
2154      * @return an array containing 1 Object: the text of the button,
2155      *         if the item is selected; otherwise <code>null</code>
2156      */
2157     public Object[] getSelectedObjects() {
2158         if (isSelected() == false) {
2159             return null;
2160         }
2161         Object[] selectedObjects = new Object[1];
2162         selectedObjects[0] = getText();
2163         return selectedObjects;
2164     }
2165 
2166     protected void init(String text, Icon icon) {
2167         if(text != null) {
2168             setText(text);
2169         }
2170 
2171         if(icon != null) {
2172             setIcon(icon);
2173         }
2174 
2175         // Set the UI
2176         updateUI();
2177 
2178         setAlignmentX(LEFT_ALIGNMENT);
2179         setAlignmentY(CENTER_ALIGNMENT);
2180     }
2181 
2182 
2183     /**
2184      * This is overridden to return false if the current <code>Icon</code>'s
2185      * <code>Image</code> is not equal to the
2186      * passed in <code>Image</code> <code>img</code>.
2187      *
2188      * @param img  the <code>Image</code> to be compared
2189      * @param infoflags flags used to repaint the button when the image
2190      *          is updated and which determine how much is to be painted
2191      * @param x  the x coordinate
2192      * @param y  the y coordinate
2193      * @param w  the width
2194      * @param h  the height
2195      * @see     java.awt.image.ImageObserver
2196      * @see     java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
2197      */
2198     public boolean imageUpdate(Image img, int infoflags,
2199                                int x, int y, int w, int h) {
2200         Icon iconDisplayed = getIcon();
2201         if (iconDisplayed == null) {
2202             return false;
2203         }
2204 
2205         if (!model.isEnabled()) {
2206             if (model.isSelected()) {
2207                 iconDisplayed = getDisabledSelectedIcon();
2208             } else {
2209                 iconDisplayed = getDisabledIcon();
2210             }
2211         } else if (model.isPressed() && model.isArmed()) {
2212             iconDisplayed = getPressedIcon();
2213         } else if (isRolloverEnabled() && model.isRollover()) {
2214             if (model.isSelected()) {
2215                 iconDisplayed = getRolloverSelectedIcon();
2216             } else {
2217                 iconDisplayed = getRolloverIcon();
2218             }
2219         } else if (model.isSelected()) {
2220             iconDisplayed = getSelectedIcon();
2221         }
2222 
2223         if (!SwingUtilities.doesIconReferenceImage(iconDisplayed, img)) {
2224             // We don't know about this image, disable the notification so
2225             // we don't keep repainting.
2226             return false;
2227         }
2228         return super.imageUpdate(img, infoflags, x, y, w, h);
2229     }
2230 
2231     void setUIProperty(String propertyName, Object value) {
2232         if (propertyName == "borderPainted") {
2233             if (!borderPaintedSet) {
2234                 setBorderPainted(((Boolean)value).booleanValue());
2235                 borderPaintedSet = false;
2236             }
2237         } else if (propertyName == "rolloverEnabled") {
2238             if (!rolloverEnabledSet) {
2239                 setRolloverEnabled(((Boolean)value).booleanValue());
2240                 rolloverEnabledSet = false;
2241             }
2242         } else if (propertyName == "iconTextGap") {
2243             if (!iconTextGapSet) {
2244                 setIconTextGap(((Number)value).intValue());
2245                 iconTextGapSet = false;
2246             }
2247         } else if (propertyName == "contentAreaFilled") {
2248             if (!contentAreaFilledSet) {
2249                 setContentAreaFilled(((Boolean)value).booleanValue());
2250                 contentAreaFilledSet = false;
2251             }
2252         } else {
2253             super.setUIProperty(propertyName, value);
2254         }
2255     }
2256 
2257     /**
2258      * Returns a string representation of this <code>AbstractButton</code>.
2259      * This method
2260      * is intended to be used only for debugging purposes, and the
2261      * content and format of the returned string may vary between
2262      * implementations. The returned string may be empty but may not
2263      * be <code>null</code>.
2264      * <P>
2265      * Overriding <code>paramString</code> to provide information about the
2266      * specific new aspects of the JFC components.
2267      *
2268      * @return  a string representation of this <code>AbstractButton</code>
2269      */
2270     protected String paramString() {
2271         String defaultIconString = ((defaultIcon != null)
2272                                     && (defaultIcon != this) ?
2273                                     defaultIcon.toString() : "");
2274         String pressedIconString = ((pressedIcon != null)
2275                                     && (pressedIcon != this) ?
2276                                     pressedIcon.toString() : "");
2277         String disabledIconString = ((disabledIcon != null)
2278                                      && (disabledIcon != this) ?
2279                                      disabledIcon.toString() : "");
2280         String selectedIconString = ((selectedIcon != null)
2281                                      && (selectedIcon != this) ?
2282                                      selectedIcon.toString() : "");
2283         String disabledSelectedIconString = ((disabledSelectedIcon != null) &&
2284                                              (disabledSelectedIcon != this) ?
2285                                              disabledSelectedIcon.toString()
2286                                              : "");
2287         String rolloverIconString = ((rolloverIcon != null)
2288                                      && (rolloverIcon != this) ?
2289                                      rolloverIcon.toString() : "");
2290         String rolloverSelectedIconString = ((rolloverSelectedIcon != null) &&
2291                                              (rolloverSelectedIcon != this) ?
2292                                              rolloverSelectedIcon.toString()
2293                                              : "");
2294         String paintBorderString = (paintBorder ? "true" : "false");
2295         String paintFocusString = (paintFocus ? "true" : "false");
2296         String rolloverEnabledString = (rolloverEnabled ? "true" : "false");
2297 
2298         return super.paramString() +
2299         ",defaultIcon=" + defaultIconString +
2300         ",disabledIcon=" + disabledIconString +
2301         ",disabledSelectedIcon=" + disabledSelectedIconString +
2302         ",margin=" + margin +
2303         ",paintBorder=" + paintBorderString +
2304         ",paintFocus=" + paintFocusString +
2305         ",pressedIcon=" + pressedIconString +
2306         ",rolloverEnabled=" + rolloverEnabledString +
2307         ",rolloverIcon=" + rolloverIconString +
2308         ",rolloverSelectedIcon=" + rolloverSelectedIconString +
2309         ",selectedIcon=" + selectedIconString +
2310         ",text=" + text;
2311     }
2312 
2313 
2314     private Handler getHandler() {
2315         if (handler == null) {
2316             handler = new Handler();
2317         }
2318         return handler;
2319     }
2320 
2321 
2322     //
2323     // Listeners that are added to model
2324     //
2325     @SuppressWarnings("serial")
2326     class Handler implements ActionListener, ChangeListener, ItemListener,
2327                              Serializable {
2328         //
2329         // ChangeListener
2330         //
2331         public void stateChanged(ChangeEvent e) {
2332             Object source = e.getSource();
2333 
2334             updateMnemonicProperties();
2335             if (isEnabled() != model.isEnabled()) {
2336                 setEnabled(model.isEnabled());
2337             }
2338             fireStateChanged();
2339             repaint();
2340         }
2341 
2342         //
2343         // ActionListener
2344         //
2345         public void actionPerformed(ActionEvent event) {
2346             fireActionPerformed(event);
2347         }
2348 
2349         //
2350         // ItemListener
2351         //
2352         public void itemStateChanged(ItemEvent event) {
2353             fireItemStateChanged(event);
2354             if (shouldUpdateSelectedStateFromAction()) {
2355                 Action action = getAction();
2356                 if (action != null && AbstractAction.hasSelectedKey(action)) {
2357                     boolean selected = isSelected();
2358                     boolean isActionSelected = AbstractAction.isSelected(
2359                               action);
2360                     if (isActionSelected != selected) {
2361                         action.putValue(Action.SELECTED_KEY, selected);
2362                     }
2363                 }
2364             }
2365         }
2366     }
2367 
2368 ///////////////////
2369 // Accessibility support
2370 ///////////////////
2371     /**
2372      * This class implements accessibility support for the
2373      * <code>AbstractButton</code> class.  It provides an implementation of the
2374      * Java Accessibility API appropriate to button and menu item
2375      * user-interface elements.
2376      * <p>
2377      * <strong>Warning:</strong>
2378      * Serialized objects of this class will not be compatible with
2379      * future Swing releases. The current serialization support is
2380      * appropriate for short term storage or RMI between applications running
2381      * the same version of Swing.  As of 1.4, support for long term storage
2382      * of all JavaBeans&trade;
2383      * has been added to the <code>java.beans</code> package.
2384      * Please see {@link java.beans.XMLEncoder}.
2385      * @since 1.4
2386      */
2387     protected abstract class AccessibleAbstractButton
2388         extends AccessibleJComponent implements AccessibleAction,
2389         AccessibleValue, AccessibleText, AccessibleExtendedComponent {
2390 
2391         /**
2392          * Returns the accessible name of this object.
2393          *
2394          * @return the localized name of the object -- can be
2395          *              <code>null</code> if this
2396          *              object does not have a name
2397          */
2398         public String getAccessibleName() {
2399             String name = accessibleName;
2400 
2401             if (name == null) {
2402                 name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
2403             }
2404             if (name == null) {
2405                 name = AbstractButton.this.getText();
2406             }
2407             if (name == null) {
2408                 name = super.getAccessibleName();
2409             }
2410             return name;
2411         }
2412 
2413         /**
2414          * Get the AccessibleIcons associated with this object if one
2415          * or more exist.  Otherwise return null.
2416          * @since 1.3
2417          */
2418         public AccessibleIcon [] getAccessibleIcon() {
2419             Icon defaultIcon = getIcon();
2420 
2421             if (defaultIcon instanceof Accessible) {
2422                 AccessibleContext ac =
2423                     ((Accessible)defaultIcon).getAccessibleContext();
2424                 if (ac != null && ac instanceof AccessibleIcon) {
2425                     return new AccessibleIcon[] { (AccessibleIcon)ac };
2426                 }
2427             }
2428             return null;
2429         }
2430 
2431         /**
2432          * Get the state set of this object.
2433          *
2434          * @return an instance of AccessibleState containing the current state
2435          * of the object
2436          * @see AccessibleState
2437          */
2438         public AccessibleStateSet getAccessibleStateSet() {
2439         AccessibleStateSet states = super.getAccessibleStateSet();
2440             if (getModel().isArmed()) {
2441                 states.add(AccessibleState.ARMED);
2442             }
2443             if (isFocusOwner()) {
2444                 states.add(AccessibleState.FOCUSED);
2445             }
2446             if (getModel().isPressed()) {
2447                 states.add(AccessibleState.PRESSED);
2448             }
2449             if (isSelected()) {
2450                 states.add(AccessibleState.CHECKED);
2451             }
2452             return states;
2453         }
2454 
2455         /**
2456          * Get the AccessibleRelationSet associated with this object if one
2457          * exists.  Otherwise return null.
2458          * @see AccessibleRelation
2459          * @since 1.3
2460          */
2461         public AccessibleRelationSet getAccessibleRelationSet() {
2462 
2463             // Check where the AccessibleContext's relation
2464             // set already contains a MEMBER_OF relation.
2465             AccessibleRelationSet relationSet
2466                 = super.getAccessibleRelationSet();
2467 
2468             if (!relationSet.contains(AccessibleRelation.MEMBER_OF)) {
2469                 // get the members of the button group if one exists
2470                 ButtonModel model = getModel();
2471                 if (model != null && model instanceof DefaultButtonModel) {
2472                     ButtonGroup group = ((DefaultButtonModel)model).getGroup();
2473                     if (group != null) {
2474                         // set the target of the MEMBER_OF relation to be
2475                         // the members of the button group.
2476                         int len = group.getButtonCount();
2477                         Object [] target = new Object[len];
2478                         Enumeration<AbstractButton> elem = group.getElements();
2479                         for (int i = 0; i < len; i++) {
2480                             if (elem.hasMoreElements()) {
2481                                 target[i] = elem.nextElement();
2482                             }
2483                         }
2484                         AccessibleRelation relation =
2485                             new AccessibleRelation(AccessibleRelation.MEMBER_OF);
2486                         relation.setTarget(target);
2487                         relationSet.add(relation);
2488                     }
2489                 }
2490             }
2491             return relationSet;
2492         }
2493 
2494         /**
2495          * Get the AccessibleAction associated with this object.  In the
2496          * implementation of the Java Accessibility API for this class,
2497          * return this object, which is responsible for implementing the
2498          * AccessibleAction interface on behalf of itself.
2499          *
2500          * @return this object
2501          */
2502         public AccessibleAction getAccessibleAction() {
2503             return this;
2504         }
2505 
2506         /**
2507          * Get the AccessibleValue associated with this object.  In the
2508          * implementation of the Java Accessibility API for this class,
2509          * return this object, which is responsible for implementing the
2510          * AccessibleValue interface on behalf of itself.
2511          *
2512          * @return this object
2513          */
2514         public AccessibleValue getAccessibleValue() {
2515             return this;
2516         }
2517 
2518         /**
2519          * Returns the number of Actions available in this object.  The
2520          * default behavior of a button is to have one action - toggle
2521          * the button.
2522          *
2523          * @return 1, the number of Actions in this object
2524          */
2525         public int getAccessibleActionCount() {
2526             return 1;
2527         }
2528 
2529         /**
2530          * Return a description of the specified action of the object.
2531          *
2532          * @param i zero-based index of the actions
2533          */
2534         public String getAccessibleActionDescription(int i) {
2535             if (i == 0) {
2536                 return UIManager.getString("AbstractButton.clickText");
2537             } else {
2538                 return null;
2539             }
2540         }
2541 
2542         /**
2543          * Perform the specified Action on the object
2544          *
2545          * @param i zero-based index of actions
2546          * @return true if the the action was performed; else false.
2547          */
2548         public boolean doAccessibleAction(int i) {
2549             if (i == 0) {
2550                 doClick();
2551                 return true;
2552             } else {
2553                 return false;
2554             }
2555         }
2556 
2557         /**
2558          * Get the value of this object as a Number.
2559          *
2560          * @return An Integer of 0 if this isn't selected or an Integer of 1 if
2561          * this is selected.
2562          * @see AbstractButton#isSelected
2563          */
2564         public Number getCurrentAccessibleValue() {
2565             if (isSelected()) {
2566                 return Integer.valueOf(1);
2567             } else {
2568                 return Integer.valueOf(0);
2569             }
2570         }
2571 
2572         /**
2573          * Set the value of this object as a Number.
2574          *
2575          * @return True if the value was set.
2576          */
2577         public boolean setCurrentAccessibleValue(Number n) {
2578             // TIGER - 4422535
2579             if (n == null) {
2580                 return false;
2581             }
2582             int i = n.intValue();
2583             if (i == 0) {
2584                 setSelected(false);
2585             } else {
2586                 setSelected(true);
2587             }
2588             return true;
2589         }
2590 
2591         /**
2592          * Get the minimum value of this object as a Number.
2593          *
2594          * @return an Integer of 0.
2595          */
2596         public Number getMinimumAccessibleValue() {
2597             return Integer.valueOf(0);
2598         }
2599 
2600         /**
2601          * Get the maximum value of this object as a Number.
2602          *
2603          * @return An Integer of 1.
2604          */
2605         public Number getMaximumAccessibleValue() {
2606             return Integer.valueOf(1);
2607         }
2608 
2609 
2610         /* AccessibleText ---------- */
2611 
2612         public AccessibleText getAccessibleText() {
2613             View view = (View)AbstractButton.this.getClientProperty("html");
2614             if (view != null) {
2615                 return this;
2616             } else {
2617                 return null;
2618             }
2619         }
2620 
2621         /**
2622          * Given a point in local coordinates, return the zero-based index
2623          * of the character under that Point.  If the point is invalid,
2624          * this method returns -1.
2625          *
2626          * Note: the AbstractButton must have a valid size (e.g. have
2627          * been added to a parent container whose ancestor container
2628          * is a valid top-level window) for this method to be able
2629          * to return a meaningful value.
2630          *
2631          * @param p the Point in local coordinates
2632          * @return the zero-based index of the character under Point p; if
2633          * Point is invalid returns -1.
2634          * @since 1.3
2635          */
2636         public int getIndexAtPoint(Point p) {
2637             View view = (View) AbstractButton.this.getClientProperty("html");
2638             if (view != null) {
2639                 Rectangle r = getTextRectangle();
2640                 if (r == null) {
2641                     return -1;
2642                 }
2643                 Rectangle2D.Float shape =
2644                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
2645                 Position.Bias bias[] = new Position.Bias[1];
2646                 return view.viewToModel(p.x, p.y, shape, bias);
2647             } else {
2648                 return -1;
2649             }
2650         }
2651 
2652         /**
2653          * Determine the bounding box of the character at the given
2654          * index into the string.  The bounds are returned in local
2655          * coordinates.  If the index is invalid an empty rectangle is
2656          * returned.
2657          *
2658          * Note: the AbstractButton must have a valid size (e.g. have
2659          * been added to a parent container whose ancestor container
2660          * is a valid top-level window) for this method to be able
2661          * to return a meaningful value.
2662          *
2663          * @param i the index into the String
2664          * @return the screen coordinates of the character's the bounding box,
2665          * if index is invalid returns an empty rectangle.
2666          * @since 1.3
2667          */
2668         public Rectangle getCharacterBounds(int i) {
2669             View view = (View) AbstractButton.this.getClientProperty("html");
2670             if (view != null) {
2671                 Rectangle r = getTextRectangle();
2672                 if (r == null) {
2673                     return null;
2674                 }
2675                 Rectangle2D.Float shape =
2676                     new Rectangle2D.Float(r.x, r.y, r.width, r.height);
2677                 try {
2678                     Shape charShape =
2679                         view.modelToView(i, shape, Position.Bias.Forward);
2680                     return charShape.getBounds();
2681                 } catch (BadLocationException e) {
2682                     return null;
2683                 }
2684             } else {
2685                 return null;
2686             }
2687         }
2688 
2689         /**
2690          * Return the number of characters (valid indicies)
2691          *
2692          * @return the number of characters
2693          * @since 1.3
2694          */
2695         public int getCharCount() {
2696             View view = (View) AbstractButton.this.getClientProperty("html");
2697             if (view != null) {
2698                 Document d = view.getDocument();
2699                 if (d instanceof StyledDocument) {
2700                     StyledDocument doc = (StyledDocument)d;
2701                     return doc.getLength();
2702                 }
2703             }
2704             return accessibleContext.getAccessibleName().length();
2705         }
2706 
2707         /**
2708          * Return the zero-based offset of the caret.
2709          *
2710          * Note: That to the right of the caret will have the same index
2711          * value as the offset (the caret is between two characters).
2712          * @return the zero-based offset of the caret.
2713          * @since 1.3
2714          */
2715         public int getCaretPosition() {
2716             // There is no caret.
2717             return -1;
2718         }
2719 
2720         /**
2721          * Returns the String at a given index.
2722          *
2723          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
2724          * or AccessibleText.SENTENCE to retrieve
2725          * @param index an index within the text &gt;= 0
2726          * @return the letter, word, or sentence,
2727          *   null for an invalid index or part
2728          * @since 1.3
2729          */
2730         public String getAtIndex(int part, int index) {
2731             if (index < 0 || index >= getCharCount()) {
2732                 return null;
2733             }
2734             switch (part) {
2735             case AccessibleText.CHARACTER:
2736                 try {
2737                     return getText(index, 1);
2738                 } catch (BadLocationException e) {
2739                     return null;
2740                 }
2741             case AccessibleText.WORD:
2742                 try {
2743                     String s = getText(0, getCharCount());
2744                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
2745                     words.setText(s);
2746                     int end = words.following(index);
2747                     return s.substring(words.previous(), end);
2748                 } catch (BadLocationException e) {
2749                     return null;
2750                 }
2751             case AccessibleText.SENTENCE:
2752                 try {
2753                     String s = getText(0, getCharCount());
2754                     BreakIterator sentence =
2755                         BreakIterator.getSentenceInstance(getLocale());
2756                     sentence.setText(s);
2757                     int end = sentence.following(index);
2758                     return s.substring(sentence.previous(), end);
2759                 } catch (BadLocationException e) {
2760                     return null;
2761                 }
2762             default:
2763                 return null;
2764             }
2765         }
2766 
2767         /**
2768          * Returns the String after a given index.
2769          *
2770          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
2771          * or AccessibleText.SENTENCE to retrieve
2772          * @param index an index within the text &gt;= 0
2773          * @return the letter, word, or sentence, null for an invalid
2774          *  index or part
2775          * @since 1.3
2776          */
2777         public String getAfterIndex(int part, int index) {
2778             if (index < 0 || index >= getCharCount()) {
2779                 return null;
2780             }
2781             switch (part) {
2782             case AccessibleText.CHARACTER:
2783                 if (index+1 >= getCharCount()) {
2784                    return null;
2785                 }
2786                 try {
2787                     return getText(index+1, 1);
2788                 } catch (BadLocationException e) {
2789                     return null;
2790                 }
2791             case AccessibleText.WORD:
2792                 try {
2793                     String s = getText(0, getCharCount());
2794                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
2795                     words.setText(s);
2796                     int start = words.following(index);
2797                     if (start == BreakIterator.DONE || start >= s.length()) {
2798                         return null;
2799                     }
2800                     int end = words.following(start);
2801                     if (end == BreakIterator.DONE || end >= s.length()) {
2802                         return null;
2803                     }
2804                     return s.substring(start, end);
2805                 } catch (BadLocationException e) {
2806                     return null;
2807                 }
2808             case AccessibleText.SENTENCE:
2809                 try {
2810                     String s = getText(0, getCharCount());
2811                     BreakIterator sentence =
2812                         BreakIterator.getSentenceInstance(getLocale());
2813                     sentence.setText(s);
2814                     int start = sentence.following(index);
2815                     if (start == BreakIterator.DONE || start > s.length()) {
2816                         return null;
2817                     }
2818                     int end = sentence.following(start);
2819                     if (end == BreakIterator.DONE || end > s.length()) {
2820                         return null;
2821                     }
2822                     return s.substring(start, end);
2823                 } catch (BadLocationException e) {
2824                     return null;
2825                 }
2826             default:
2827                 return null;
2828             }
2829         }
2830 
2831         /**
2832          * Returns the String before a given index.
2833          *
2834          * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
2835          *   or AccessibleText.SENTENCE to retrieve
2836          * @param index an index within the text &gt;= 0
2837          * @return the letter, word, or sentence, null for an invalid index
2838          *  or part
2839          * @since 1.3
2840          */
2841         public String getBeforeIndex(int part, int index) {
2842             if (index < 0 || index > getCharCount()-1) {
2843                 return null;
2844             }
2845             switch (part) {
2846             case AccessibleText.CHARACTER:
2847                 if (index == 0) {
2848                     return null;
2849                 }
2850                 try {
2851                     return getText(index-1, 1);
2852                 } catch (BadLocationException e) {
2853                     return null;
2854                 }
2855             case AccessibleText.WORD:
2856                 try {
2857                     String s = getText(0, getCharCount());
2858                     BreakIterator words = BreakIterator.getWordInstance(getLocale());
2859                     words.setText(s);
2860                     int end = words.following(index);
2861                     end = words.previous();
2862                     int start = words.previous();
2863                     if (start == BreakIterator.DONE) {
2864                         return null;
2865                     }
2866                     return s.substring(start, end);
2867                 } catch (BadLocationException e) {
2868                     return null;
2869                 }
2870             case AccessibleText.SENTENCE:
2871                 try {
2872                     String s = getText(0, getCharCount());
2873                     BreakIterator sentence =
2874                         BreakIterator.getSentenceInstance(getLocale());
2875                     sentence.setText(s);
2876                     int end = sentence.following(index);
2877                     end = sentence.previous();
2878                     int start = sentence.previous();
2879                     if (start == BreakIterator.DONE) {
2880                         return null;
2881                     }
2882                     return s.substring(start, end);
2883                 } catch (BadLocationException e) {
2884                     return null;
2885                 }
2886             default:
2887                 return null;
2888             }
2889         }
2890 
2891         /**
2892          * Return the AttributeSet for a given character at a given index
2893          *
2894          * @param i the zero-based index into the text
2895          * @return the AttributeSet of the character
2896          * @since 1.3
2897          */
2898         public AttributeSet getCharacterAttribute(int i) {
2899             View view = (View) AbstractButton.this.getClientProperty("html");
2900             if (view != null) {
2901                 Document d = view.getDocument();
2902                 if (d instanceof StyledDocument) {
2903                     StyledDocument doc = (StyledDocument)d;
2904                     Element elem = doc.getCharacterElement(i);
2905                     if (elem != null) {
2906                         return elem.getAttributes();
2907                     }
2908                 }
2909             }
2910             return null;
2911         }
2912 
2913         /**
2914          * Returns the start offset within the selected text.
2915          * If there is no selection, but there is
2916          * a caret, the start and end offsets will be the same.
2917          *
2918          * @return the index into the text of the start of the selection
2919          * @since 1.3
2920          */
2921         public int getSelectionStart() {
2922             // Text cannot be selected.
2923             return -1;
2924         }
2925 
2926         /**
2927          * Returns the end offset within the selected text.
2928          * If there is no selection, but there is
2929          * a caret, the start and end offsets will be the same.
2930          *
2931          * @return the index into the text of the end of the selection
2932          * @since 1.3
2933          */
2934         public int getSelectionEnd() {
2935             // Text cannot be selected.
2936             return -1;
2937         }
2938 
2939         /**
2940          * Returns the portion of the text that is selected.
2941          *
2942          * @return the String portion of the text that is selected
2943          * @since 1.3
2944          */
2945         public String getSelectedText() {
2946             // Text cannot be selected.
2947             return null;
2948         }
2949 
2950         /*
2951          * Returns the text substring starting at the specified
2952          * offset with the specified length.
2953          */
2954         private String getText(int offset, int length)
2955             throws BadLocationException {
2956 
2957             View view = (View) AbstractButton.this.getClientProperty("html");
2958             if (view != null) {
2959                 Document d = view.getDocument();
2960                 if (d instanceof StyledDocument) {
2961                     StyledDocument doc = (StyledDocument)d;
2962                     return doc.getText(offset, length);
2963                 }
2964             }
2965             return null;
2966         }
2967 
2968         /*
2969          * Returns the bounding rectangle for the component text.
2970          */
2971         private Rectangle getTextRectangle() {
2972 
2973             String text = AbstractButton.this.getText();
2974             Icon icon = (AbstractButton.this.isEnabled()) ? AbstractButton.this.getIcon() : AbstractButton.this.getDisabledIcon();
2975 
2976             if ((icon == null) && (text == null)) {
2977                 return null;
2978             }
2979 
2980             Rectangle paintIconR = new Rectangle();
2981             Rectangle paintTextR = new Rectangle();
2982             Rectangle paintViewR = new Rectangle();
2983             Insets paintViewInsets = new Insets(0, 0, 0, 0);
2984 
2985             paintViewInsets = AbstractButton.this.getInsets(paintViewInsets);
2986             paintViewR.x = paintViewInsets.left;
2987             paintViewR.y = paintViewInsets.top;
2988             paintViewR.width = AbstractButton.this.getWidth() - (paintViewInsets.left + paintViewInsets.right);
2989             paintViewR.height = AbstractButton.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
2990 
2991             String clippedText = SwingUtilities.layoutCompoundLabel(
2992                 AbstractButton.this,
2993                 getFontMetrics(getFont()),
2994                 text,
2995                 icon,
2996                 AbstractButton.this.getVerticalAlignment(),
2997                 AbstractButton.this.getHorizontalAlignment(),
2998                 AbstractButton.this.getVerticalTextPosition(),
2999                 AbstractButton.this.getHorizontalTextPosition(),
3000                 paintViewR,
3001                 paintIconR,
3002                 paintTextR,
3003                 0);
3004 
3005             return paintTextR;
3006         }
3007 
3008         // ----- AccessibleExtendedComponent
3009 
3010         /**
3011          * Returns the AccessibleExtendedComponent
3012          *
3013          * @return the AccessibleExtendedComponent
3014          */
3015         AccessibleExtendedComponent getAccessibleExtendedComponent() {
3016             return this;
3017         }
3018 
3019         /**
3020          * Returns the tool tip text
3021          *
3022          * @return the tool tip text, if supported, of the object;
3023          * otherwise, null
3024          * @since 1.4
3025          */
3026         public String getToolTipText() {
3027             return AbstractButton.this.getToolTipText();
3028         }
3029 
3030         /**
3031          * Returns the titled border text
3032          *
3033          * @return the titled border text, if supported, of the object;
3034          * otherwise, null
3035          * @since 1.4
3036          */
3037         public String getTitledBorderText() {
3038             return super.getTitledBorderText();
3039         }
3040 
3041         /**
3042          * Returns key bindings associated with this object
3043          *
3044          * @return the key bindings, if supported, of the object;
3045          * otherwise, null
3046          * @see AccessibleKeyBinding
3047          * @since 1.4
3048          */
3049         public AccessibleKeyBinding getAccessibleKeyBinding() {
3050             int mnemonic = AbstractButton.this.getMnemonic();
3051             if (mnemonic == 0) {
3052                 return null;
3053             }
3054             return new ButtonKeyBinding(mnemonic);
3055         }
3056 
3057         class ButtonKeyBinding implements AccessibleKeyBinding {
3058             int mnemonic;
3059 
3060             ButtonKeyBinding(int mnemonic) {
3061                 this.mnemonic = mnemonic;
3062             }
3063 
3064             /**
3065              * Returns the number of key bindings for this object
3066              *
3067              * @return the zero-based number of key bindings for this object
3068              */
3069             public int getAccessibleKeyBindingCount() {
3070                 return 1;
3071             }
3072 
3073             /**
3074              * Returns a key binding for this object.  The value returned is an
3075              * java.lang.Object which must be cast to appropriate type depending
3076              * on the underlying implementation of the key.  For example, if the
3077              * Object returned is a javax.swing.KeyStroke, the user of this
3078              * method should do the following:
3079              * <nf><code>
3080              * Component c = <get the component that has the key bindings>
3081              * AccessibleContext ac = c.getAccessibleContext();
3082              * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
3083              * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
3084              *     Object o = akb.getAccessibleKeyBinding(i);
3085              *     if (o instanceof javax.swing.KeyStroke) {
3086              *         javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
3087              *         <do something with the key binding>
3088              *     }
3089              * }
3090              * </code></nf>
3091              *
3092              * @param i zero-based index of the key bindings
3093              * @return a javax.lang.Object which specifies the key binding
3094              * @exception IllegalArgumentException if the index is
3095              * out of bounds
3096              * @see #getAccessibleKeyBindingCount
3097              */
3098             public java.lang.Object getAccessibleKeyBinding(int i) {
3099                 if (i != 0) {
3100                     throw new IllegalArgumentException();
3101                 }
3102                 return KeyStroke.getKeyStroke(mnemonic, 0);
3103             }
3104         }
3105     }
3106 }