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