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