Source for javax.swing.AbstractButton

   1: /* AbstractButton.java -- Provides basic button functionality.
   2:    Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package javax.swing;
  39: 
  40: import gnu.java.lang.CPStringBuilder;
  41: 
  42: import java.awt.Component;
  43: import java.awt.Graphics;
  44: import java.awt.Image;
  45: import java.awt.Insets;
  46: import java.awt.ItemSelectable;
  47: import java.awt.LayoutManager;
  48: import java.awt.Point;
  49: import java.awt.Rectangle;
  50: import java.awt.Shape;
  51: import java.awt.event.ActionEvent;
  52: import java.awt.event.ActionListener;
  53: import java.awt.event.ItemEvent;
  54: import java.awt.event.ItemListener;
  55: import java.awt.image.ImageObserver;
  56: import java.beans.PropertyChangeEvent;
  57: import java.beans.PropertyChangeListener;
  58: import java.io.Serializable;
  59: import java.util.Enumeration;
  60: 
  61: import javax.accessibility.Accessible;
  62: import javax.accessibility.AccessibleAction;
  63: import javax.accessibility.AccessibleContext;
  64: import javax.accessibility.AccessibleIcon;
  65: import javax.accessibility.AccessibleRelation;
  66: import javax.accessibility.AccessibleRelationSet;
  67: import javax.accessibility.AccessibleState;
  68: import javax.accessibility.AccessibleStateSet;
  69: import javax.accessibility.AccessibleText;
  70: import javax.accessibility.AccessibleValue;
  71: import javax.swing.event.ChangeEvent;
  72: import javax.swing.event.ChangeListener;
  73: import javax.swing.plaf.ButtonUI;
  74: import javax.swing.plaf.basic.BasicHTML;
  75: import javax.swing.text.AttributeSet;
  76: import javax.swing.text.BadLocationException;
  77: import javax.swing.text.Document;
  78: import javax.swing.text.Element;
  79: import javax.swing.text.Position;
  80: import javax.swing.text.StyledDocument;
  81: import javax.swing.text.View;
  82: 
  83: 
  84: /**
  85:  * Provides an abstract implementation of common button behaviour,
  86:  * data model and look & feel.
  87:  *
  88:  * <p>This class is supposed to serve as a base class for
  89:  * several kinds of buttons with similar but non-identical semantics:
  90:  * toggle buttons (radio buttons and checkboxes), simple push buttons,
  91:  * menu items, etc.</p>
  92:  *
  93:  * <p>Buttons have many properties, some of which are stored in this class
  94:  * while others are delegated to the button's model. The following properties
  95:  * are available:</p>
  96:  *
  97:  * <table>
  98:  * <tr><th>Property               </th><th>Stored in</th><th>Bound?</th></tr>
  99:  *
 100:  * <tr><td>action                 </td><td>button</td> <td>no</td></tr>
 101:  * <tr><td>actionCommand          </td><td>model</td>  <td>no</td></tr>
 102:  * <tr><td>borderPainted          </td><td>button</td> <td>yes</td></tr>
 103:  * <tr><td>contentAreaFilled      </td><td>button</td> <td>yes</td></tr>
 104:  * <tr><td>disabledIcon           </td><td>button</td> <td>yes</td></tr>
 105:  * <tr><td>disabledSelectedIcon   </td><td>button</td> <td>yes</td></tr>
 106:  * <tr><td>displayedMnemonicIndex </td><td>button</td> <td>no</td></tr>
 107:  * <tr><td>enabled                </td><td>model</td>  <td>no</td></tr>
 108:  * <tr><td>focusPainted           </td><td>button</td> <td>yes</td></tr>
 109:  * <tr><td>horizontalAlignment    </td><td>button</td> <td>yes</td></tr>
 110:  * <tr><td>horizontalTextPosition </td><td>button</td> <td>yes</td></tr>
 111:  * <tr><td>icon                   </td><td>button</td> <td>yes</td></tr>
 112:  * <tr><td>iconTextGap            </td><td>button</td> <td>no</td></tr>
 113:  * <tr><td>label (same as text)   </td><td>model</td>  <td>yes</td></tr>
 114:  * <tr><td>margin                 </td><td>button</td> <td>yes</td></tr>
 115:  * <tr><td>multiClickThreshold    </td><td>button</td> <td>no</td></tr>
 116:  * <tr><td>pressedIcon            </td><td>button</td> <td>yes</td></tr>
 117:  * <tr><td>rolloverEnabled        </td><td>button</td> <td>yes</td></tr>
 118:  * <tr><td>rolloverIcon           </td><td>button</td> <td>yes</td></tr>
 119:  * <tr><td>rolloverSelectedIcon   </td><td>button</td> <td>yes</td></tr>
 120:  * <tr><td>selected               </td><td>model</td>  <td>no</td></tr>
 121:  * <tr><td>selectedIcon           </td><td>button</td> <td>yes</td></tr>
 122:  * <tr><td>selectedObjects        </td><td>button</td> <td>no</td></tr>
 123:  * <tr><td>text                   </td><td>model</td>  <td>yes</td></tr>
 124:  * <tr><td>UI                     </td><td>button</td> <td>yes</td></tr>
 125:  * <tr><td>verticalAlignment      </td><td>button</td> <td>yes</td></tr>
 126:  * <tr><td>verticalTextPosition   </td><td>button</td> <td>yes</td></tr>
 127:  *
 128:  * </table>
 129:  *
 130:  * <p>The various behavioral aspects of these properties follows:</p>
 131:  *
 132:  * <ul>
 133:  *
 134:  * <li>When non-bound properties stored in the button change, the button
 135:  * fires ChangeEvents to its ChangeListeners.</li>
 136:  *
 137:  * <li>When bound properties stored in the button change, the button fires
 138:  * PropertyChangeEvents to its PropertyChangeListeners</li>
 139:  *
 140:  * <li>If any of the model's properties change, it fires a ChangeEvent to
 141:  * its ChangeListeners, which include the button.</li>
 142:  *
 143:  * <li>If the button receives a ChangeEvent from its model, it will
 144:  * propagate the ChangeEvent to its ChangeListeners, with the ChangeEvent's
 145:  * "source" property set to refer to the button, rather than the model. The
 146:  * the button will request a repaint, to paint its updated state.</li>
 147:  *
 148:  * <li>If the model's "selected" property changes, the model will fire an
 149:  * ItemEvent to its ItemListeners, which include the button, in addition to
 150:  * the ChangeEvent which models the property change. The button propagates
 151:  * ItemEvents directly to its ItemListeners.</li>
 152:  *
 153:  * <li>If the model's armed and pressed properties are simultaneously
 154:  * <code>true</code>, the model will fire an ActionEvent to its
 155:  * ActionListeners, which include the button. The button will propagate
 156:  * this ActionEvent to its ActionListeners, with the ActionEvent's "source"
 157:  * property set to refer to the button, rather than the model.</li>
 158:  *
 159:  * </ul>
 160:  *
 161:  * @author Ronald Veldema (rveldema@cs.vu.nl)
 162:  * @author Graydon Hoare (graydon@redhat.com)
 163:  */
 164: 
 165: public abstract class AbstractButton extends JComponent
 166:   implements ItemSelectable, SwingConstants
 167: {
 168:   private static final long serialVersionUID = -937921345538462020L;
 169: 
 170:   /**
 171:    * An extension of ChangeListener to be serializable.
 172:    */
 173:   protected class ButtonChangeListener
 174:     implements ChangeListener, Serializable
 175:   {
 176:     private static final long serialVersionUID = 1471056094226600578L;
 177: 
 178:     /**
 179:      * The spec has no public/protected constructor for this class, so do we.
 180:      */
 181:     ButtonChangeListener()
 182:     {
 183:       // Nothing to do here.
 184:     }
 185: 
 186:     /**
 187:      * Notified when the target of the listener changes its state.
 188:      *
 189:      * @param ev the ChangeEvent describing the change
 190:      */
 191:     public void stateChanged(ChangeEvent ev)
 192:     {
 193:       getEventHandler().stateChanged(ev);
 194:     }
 195:   }
 196: 
 197:   /**
 198:    * The combined event handler for ActionEvent, ChangeEvent and
 199:    * ItemEvent. This combines ButtonChangeListener, ActionListener
 200:    */
 201:   private class EventHandler
 202:     implements ActionListener, ChangeListener, ItemListener
 203:   {
 204:     public void actionPerformed(ActionEvent ev)
 205:     {
 206:       fireActionPerformed(ev);
 207:     }
 208: 
 209:     public void stateChanged(ChangeEvent ev)
 210:     {
 211:       fireStateChanged();
 212:       repaint();
 213:     }
 214: 
 215:     public void itemStateChanged(ItemEvent ev)
 216:     {
 217:       fireItemStateChanged(ev);
 218:     }
 219:   }
 220: 
 221:   /** The icon displayed by default. */
 222:   Icon default_icon;
 223: 
 224:   /** The icon displayed when the button is pressed. */
 225:   Icon pressed_icon;
 226: 
 227:   /** The icon displayed when the button is disabled. */
 228:   Icon disabledIcon;
 229: 
 230:   /** The icon displayed when the button is selected. */
 231:   Icon selectedIcon;
 232: 
 233:   /** The icon displayed when the button is selected but disabled. */
 234:   Icon disabledSelectedIcon;
 235: 
 236:   /** The icon displayed when the button is rolled over. */
 237:   Icon rolloverIcon;
 238: 
 239:   /** The icon displayed when the button is selected and rolled over. */
 240:   Icon rolloverSelectedIcon;
 241: 
 242:   /** The icon currently displayed. */
 243:   Icon current_icon;
 244: 
 245:   /** The text displayed in the button. */
 246:   String text;
 247: 
 248:   /**
 249:    * The gap between icon and text, if both icon and text are
 250:    * non-<code>null</code>.
 251:    */
 252:   int iconTextGap;
 253: 
 254:   /** The vertical alignment of the button's text and icon. */
 255:   int verticalAlignment;
 256: 
 257:   /** The horizontal alignment of the button's text and icon. */
 258:   int horizontalAlignment;
 259: 
 260:   /** The horizontal position of the button's text relative to its icon. */
 261:   int horizontalTextPosition;
 262: 
 263:   /** The vertical position of the button's text relative to its icon. */
 264:   int verticalTextPosition;
 265: 
 266:   /** Whether or not the button paints its border. */
 267:   boolean borderPainted;
 268: 
 269:   /** Whether or not the button paints its focus state. */
 270:   boolean focusPainted;
 271: 
 272:   /** Whether or not the button fills its content area. */
 273:   boolean contentAreaFilled;
 274: 
 275:   /** Whether rollover is enabled. */
 276:   boolean rollOverEnabled;
 277: 
 278:   /** The action taken when the button is clicked. */
 279:   Action action;
 280: 
 281:   /** The button's current state. */
 282:   protected ButtonModel model;
 283: 
 284:   /** The margin between the button's border and its label. */
 285:   Insets margin;
 286: 
 287:   /**
 288:    * A hint to the look and feel class, suggesting which character in the
 289:    * button's label should be underlined when drawing the label.
 290:    */
 291:   int mnemonicIndex;
 292: 
 293:   /**
 294:    * Listener the button uses to receive ActionEvents from its model.
 295:    */
 296:   protected ActionListener actionListener;
 297: 
 298:   /**
 299:    * Listener the button uses to receive ItemEvents from its model.
 300:    */
 301:   protected ItemListener itemListener;
 302: 
 303:   /**
 304:    * Listener the button uses to receive ChangeEvents from its model.
 305:    */
 306:   protected ChangeListener changeListener;
 307: 
 308:   /**
 309:    * The event handler for ActionEvent, ItemEvent and ChangeEvent.
 310:    * This replaces the above three handlers and combines them
 311:    * into one for efficiency.
 312:    */
 313:   private EventHandler eventHandler;
 314: 
 315:   /**
 316:    * The time in milliseconds in which clicks get coalesced into a single
 317:    * <code>ActionEvent</code>.
 318:    */
 319:   long multiClickThreshhold;
 320: 
 321:   /**
 322:    * Listener the button uses to receive PropertyChangeEvents from its
 323:    * Action.
 324:    */
 325:   PropertyChangeListener actionPropertyChangeListener;
 326: 
 327:   /** ChangeEvent that is fired to button's ChangeEventListeners  */
 328:   protected ChangeEvent changeEvent = new ChangeEvent(this);
 329: 
 330:   /**
 331:    * Indicates if the borderPainted property has been set by a client
 332:    * program or by the UI.
 333:    *
 334:    * @see #setUIProperty(String, Object)
 335:    * @see LookAndFeel#installProperty(JComponent, String, Object)
 336:    */
 337:   private boolean clientBorderPaintedSet = false;
 338: 
 339:   /**
 340:    * Indicates if the rolloverEnabled property has been set by a client
 341:    * program or by the UI.
 342:    *
 343:    * @see #setUIProperty(String, Object)
 344:    * @see LookAndFeel#installProperty(JComponent, String, Object)
 345:    */
 346:   private boolean clientRolloverEnabledSet = false;
 347: 
 348:   /**
 349:    * Indicates if the iconTextGap property has been set by a client
 350:    * program or by the UI.
 351:    *
 352:    * @see #setUIProperty(String, Object)
 353:    * @see LookAndFeel#installProperty(JComponent, String, Object)
 354:    */
 355:   private boolean clientIconTextGapSet = false;
 356: 
 357:   /**
 358:    * Indicates if the contentAreaFilled property has been set by a client
 359:    * program or by the UI.
 360:    *
 361:    * @see #setUIProperty(String, Object)
 362:    * @see LookAndFeel#installProperty(JComponent, String, Object)
 363:    */
 364:   private boolean clientContentAreaFilledSet = false;
 365: 
 366:   /**
 367:    * Fired in a PropertyChangeEvent when the "borderPainted" property changes.
 368:    */
 369:   public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
 370: 
 371:   /**
 372:    * Fired in a PropertyChangeEvent when the "contentAreaFilled" property
 373:    * changes.
 374:    */
 375:   public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY =
 376:     "contentAreaFilled";
 377: 
 378:   /**
 379:    * Fired in a PropertyChangeEvent when the "disabledIcon" property changes.
 380:    */
 381:   public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
 382: 
 383:   /**
 384:    * Fired in a PropertyChangeEvent when the "disabledSelectedIcon" property
 385:    * changes.
 386:    */
 387:   public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY =
 388:     "disabledSelectedIcon";
 389: 
 390:   /**
 391:    * Fired in a PropertyChangeEvent when the "focusPainted" property changes.
 392:    */
 393:   public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
 394: 
 395:   /**
 396:    * Fired in a PropertyChangeEvent when the "horizontalAlignment" property
 397:    * changes.
 398:    */
 399:   public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY =
 400:     "horizontalAlignment";
 401: 
 402:   /**
 403:    * Fired in a PropertyChangeEvent when the "horizontalTextPosition" property
 404:    * changes.
 405:    */
 406:   public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY =
 407:     "horizontalTextPosition";
 408: 
 409:   /**
 410:    * Fired in a PropertyChangeEvent when the "icon" property changes. */
 411:   public static final String ICON_CHANGED_PROPERTY = "icon";
 412: 
 413:   /** Fired in a PropertyChangeEvent when the "margin" property changes. */
 414:   public static final String MARGIN_CHANGED_PROPERTY = "margin";
 415: 
 416:   /** Fired in a PropertyChangeEvent when the "mnemonic" property changes. */
 417:   public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
 418: 
 419:   /** Fired in a PropertyChangeEvent when the "model" property changes. */
 420:   public static final String MODEL_CHANGED_PROPERTY = "model";
 421: 
 422:   /** Fired in a PropertyChangeEvent when the "pressedIcon" property changes. */
 423:   public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
 424: 
 425:   /**
 426:    * Fired in a PropertyChangeEvent when the "rolloverEnabled" property
 427:    * changes.
 428:    */
 429:   public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY =
 430:     "rolloverEnabled";
 431: 
 432:   /**
 433:    * Fired in a PropertyChangeEvent when the "rolloverIcon" property changes.
 434:    */
 435:   public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
 436: 
 437:   /**
 438:    * Fired in a PropertyChangeEvent when the "rolloverSelectedIcon" property
 439:    * changes.
 440:    */
 441:   public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY =
 442:     "rolloverSelectedIcon";
 443: 
 444:   /**
 445:    * Fired in a PropertyChangeEvent when the "selectedIcon" property changes.
 446:    */
 447:   public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
 448: 
 449:   /** Fired in a PropertyChangeEvent when the "text" property changes. */
 450:   public static final String TEXT_CHANGED_PROPERTY = "text";
 451: 
 452:   /**
 453:    * Fired in a PropertyChangeEvent when the "verticalAlignment" property
 454:    * changes.
 455:    */
 456:   public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY =
 457:     "verticalAlignment";
 458: 
 459:   /**
 460:    * Fired in a PropertyChangeEvent when the "verticalTextPosition" property
 461:    * changes.
 462:    */
 463:   public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY =
 464:     "verticalTextPosition";
 465: 
 466:   /**
 467:    * A Java Accessibility extension of the AbstractButton.
 468:    */
 469:   protected abstract class AccessibleAbstractButton
 470:     extends AccessibleJComponent implements AccessibleAction, AccessibleValue,
 471:                                             AccessibleText
 472:   {
 473:     private static final long serialVersionUID = -5673062525319836790L;
 474: 
 475:     protected AccessibleAbstractButton()
 476:     {
 477:       // Nothing to do here yet.
 478:     }
 479: 
 480:     /**
 481:      * Returns the accessible state set of this object. In addition to the
 482:      * superclass's states, the <code>AccessibleAbstractButton</code>
 483:      * supports the following states: {@link AccessibleState#ARMED},
 484:      * {@link AccessibleState#FOCUSED}, {@link AccessibleState#PRESSED} and
 485:      * {@link AccessibleState#CHECKED}.
 486:      *
 487:      * @return the current state of this accessible object
 488:      */
 489:     public AccessibleStateSet getAccessibleStateSet()
 490:     {
 491:       AccessibleStateSet state = super.getAccessibleStateSet();
 492: 
 493:       if (getModel().isArmed())
 494:         state.add(AccessibleState.ARMED);
 495:       if (getModel().isPressed())
 496:         state.add(AccessibleState.PRESSED);
 497:       if (isSelected())
 498:         state.add(AccessibleState.CHECKED);
 499: 
 500:       return state;
 501:     }
 502: 
 503:     /**
 504:      * Returns the accessible name for the button.
 505:      */
 506:     public String getAccessibleName()
 507:     {
 508:       String result = super.getAccessibleName();
 509:       if (result == null)
 510:         result = text;
 511:       return result;
 512:     }
 513: 
 514:     /**
 515:      * Returns the accessible icons of this object. If the AbstractButton's
 516:      * icon is an Accessible, and it's AccessibleContext is an AccessibleIcon,
 517:      * then this AccessibleIcon is returned, otherwise <code>null</code>.
 518:      *
 519:      * @return the accessible icons of this object, or <code>null</code> if
 520:      *         there is no accessible icon
 521:      */
 522:     public AccessibleIcon[] getAccessibleIcon()
 523:     {
 524:       AccessibleIcon[] ret = null;
 525:       Icon icon = getIcon();
 526:       if (icon instanceof Accessible)
 527:         {
 528:           AccessibleContext ctx = ((Accessible) icon).getAccessibleContext();
 529:           if (ctx instanceof AccessibleIcon)
 530:             {
 531:               ret = new AccessibleIcon[]{ (AccessibleIcon) ctx };
 532:             }
 533:         }
 534:       return ret;
 535:     }
 536: 
 537:     /**
 538:      * Returns the accessible relations of this AccessibleAbstractButton.
 539:      * If the AbstractButton is part of a ButtonGroup, then all the buttons
 540:      * in this button group are added as targets in a MEMBER_OF relation,
 541:      * otherwise an empty relation set is returned (from super).
 542:      *
 543:      * @return the accessible relations of this AccessibleAbstractButton
 544:      */
 545:     public AccessibleRelationSet getAccessibleRelationSet()
 546:     {
 547:       AccessibleRelationSet relations = super.getAccessibleRelationSet();
 548:       ButtonModel model = getModel();
 549:       if (model instanceof DefaultButtonModel)
 550:         {
 551:           ButtonGroup group = ((DefaultButtonModel) model).getGroup();
 552:           if (group != null)
 553:             {
 554:               Object[] target = new Object[group.getButtonCount()];
 555:               Enumeration els = group.getElements();
 556: 
 557:               for (int index = 0; els.hasMoreElements(); ++index)
 558:                 {
 559:                   target[index] = els.nextElement();
 560:                 }
 561: 
 562:               AccessibleRelation rel =
 563:                 new AccessibleRelation(AccessibleRelation.MEMBER_OF);
 564:               rel.setTarget(target);
 565:               relations.add(rel);
 566:             }
 567:         }
 568:       return relations;
 569:     }
 570: 
 571:     /**
 572:      * Returns the accessible action associated with this object. For buttons,
 573:      * this will be <code>this</code>.
 574:      *
 575:      * @return <code>this</code>
 576:      */
 577:     public AccessibleAction getAccessibleAction()
 578:     {
 579:       return this;
 580:     }
 581: 
 582:     /**
 583:      * Returns the accessible value of this AccessibleAbstractButton, which
 584:      * is always <code>this</code>.
 585:      *
 586:      * @return the accessible value of this AccessibleAbstractButton, which
 587:      *         is always <code>this</code>
 588:      */
 589:     public AccessibleValue getAccessibleValue()
 590:     {
 591:       return this;
 592:     }
 593: 
 594:     /**
 595:      * Returns the number of accessible actions that are supported by this
 596:      * object. Buttons support one action by default ('press button'), so this
 597:      * method always returns <code>1</code>.
 598:      *
 599:      * @return <code>1</code>, the number of supported accessible actions
 600:      */
 601:     public int getAccessibleActionCount()
 602:     {
 603:       return 1;
 604:     }
 605: 
 606:     /**
 607:      * Returns a description for the action with the specified index or
 608:      * <code>null</code> if such action does not exist.
 609:      *
 610:      * @param actionIndex the zero based index to the actions
 611:      *
 612:      * @return a description for the action with the specified index or
 613:      *         <code>null</code> if such action does not exist
 614:      */
 615:     public String getAccessibleActionDescription(int actionIndex)
 616:     {
 617:       String descr = null;
 618:       if (actionIndex == 0)
 619:         {
 620:           // FIXME: Supply localized descriptions in the UIDefaults.
 621:           descr = UIManager.getString("AbstractButton.clickText");
 622:         }
 623:       return descr;
 624:     }
 625: 
 626:     /**
 627:      * Performs the acccessible action with the specified index on this object.
 628:      * Since buttons have only one action by default (which is to press the
 629:      * button), this method performs a 'press button' when the specified index
 630:      * is <code>0</code> and nothing otherwise.
 631:      *
 632:      * @param actionIndex a zero based index into the actions of this button
 633:      *
 634:      * @return <code>true</code> if the specified action has been performed
 635:      *         successfully, <code>false</code> otherwise
 636:      */
 637:     public boolean doAccessibleAction(int actionIndex)
 638:     {
 639:       boolean retVal = false;
 640:       if (actionIndex == 0)
 641:         {
 642:           doClick();
 643:           retVal = true;
 644:         }
 645:       return retVal;
 646:     }
 647: 
 648:     /**
 649:      * Returns the current value of this object as a number. This
 650:      * implementation returns an <code>Integer(1)</code> if the button is
 651:      * selected, <code>Integer(0)</code> if the button is not selected.
 652:      *
 653:      * @return the current value of this object as a number
 654:      */
 655:     public Number getCurrentAccessibleValue()
 656:     {
 657:       Integer retVal;
 658:       if (isSelected())
 659:         retVal = new Integer(1);
 660:       else
 661:         retVal = new Integer(0);
 662:       return retVal;
 663:     }
 664: 
 665:     /**
 666:      * Sets the current accessible value as object. If the specified number
 667:      * is 0 the button will be deselected, otherwise the button will
 668:      * be selected.
 669:      *
 670:      * @param value 0 for deselected button, other for selected button
 671:      *
 672:      * @return <code>true</code> if the value has been set, <code>false</code>
 673:      *         otherwise
 674:      */
 675:     public boolean setCurrentAccessibleValue(Number value)
 676:     {
 677:       boolean retVal = false;
 678:       if (value != null)
 679:         {
 680:           if (value.intValue() == 0)
 681:             setSelected(false);
 682:           else
 683:             setSelected(true);
 684:           retVal = true;
 685:         }
 686:       return retVal;
 687:     }
 688: 
 689:     /**
 690:      * Returns the minimum accessible value for the AccessibleAbstractButton,
 691:      * which is <code>0</code>.
 692:      *
 693:      * @return the minimimum accessible value for the AccessibleAbstractButton,
 694:      *         which is <code>0</code>
 695:      */
 696:     public Number getMinimumAccessibleValue()
 697:     {
 698:       return new Integer(0);
 699:     }
 700: 
 701:     /**
 702:      * Returns the maximum accessible value for the AccessibleAbstractButton,
 703:      * which is <code>1</code>.
 704:      *
 705:      * @return the maximum accessible value for the AccessibleAbstractButton,
 706:      *         which is <code>1</code>
 707:      */
 708:     public Number getMaximumAccessibleValue()
 709:     {
 710:       return new Integer(1);
 711:     }
 712: 
 713:     /**
 714:      * Returns the accessible text for this AccessibleAbstractButton. This
 715:      * will be <code>null</code> if the button has a non-HTML label, otherwise
 716:      * <code>this</code>.
 717:      *
 718:      * @return the accessible text for this AccessibleAbstractButton
 719:      */
 720:     public AccessibleText getAccessibleText()
 721:     {
 722:       AccessibleText accessibleText = null;
 723:       if (getClientProperty(BasicHTML.propertyKey) != null)
 724:         accessibleText = this;
 725: 
 726:       return accessibleText;
 727:     }
 728: 
 729:     /**
 730:      * Returns the index of the label's character at the specified point,
 731:      * relative to the local bounds of the button. This only works for
 732:      * HTML labels.
 733:      *
 734:      * @param p the point, relative to the buttons local bounds
 735:      *
 736:      * @return the index of the label's character at the specified point
 737:      */
 738:     public int getIndexAtPoint(Point p)
 739:     {
 740:       int index = -1;
 741:       View view = (View) getClientProperty(BasicHTML.propertyKey);
 742:       if (view != null)
 743:         {
 744:           Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
 745:           index = view.viewToModel(p.x, p.y, shape, new Position.Bias[1]);
 746:         }
 747:       return index;
 748:     }
 749: 
 750:     /**
 751:      * Returns the bounds of the character at the specified index of the
 752:      * button's label. This will only work for HTML labels.
 753:      *
 754:      * @param i the index of the character of the label
 755:      *
 756:      * @return the bounds of the character at the specified index of the
 757:      *         button's label
 758:      */
 759:     public Rectangle getCharacterBounds(int i)
 760:     {
 761:       Rectangle rect = null;
 762:       View view = (View) getClientProperty(BasicHTML.propertyKey);
 763:       if (view != null)
 764:         {
 765:           Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
 766:           try
 767:             {
 768:               Shape s = view.modelToView(i, shape, Position.Bias.Forward);
 769:               rect = s.getBounds();
 770:             }
 771:           catch (BadLocationException ex)
 772:             {
 773:               rect = null;
 774:             }
 775:         }
 776:       return rect;
 777:     }
 778: 
 779:     /**
 780:      * Returns the number of characters in the button's label.
 781:      *
 782:      * @return the bounds of the character at the specified index of the
 783:      *         button's label
 784:      */
 785:     public int getCharCount()
 786:     {
 787:       int charCount;
 788:       View view = (View) getClientProperty(BasicHTML.propertyKey);
 789:       if (view != null)
 790:         {
 791:           charCount = view.getDocument().getLength();
 792:         }
 793:       else
 794:         {
 795:           charCount = getAccessibleName().length();
 796:         }
 797:       return charCount;
 798:     }
 799: 
 800:     /**
 801:      * This always returns <code>-1</code> since there is no caret in a button.
 802:      *
 803:      * @return <code>-1</code> since there is no caret in a button
 804:      */
 805:     public int getCaretPosition()
 806:     {
 807:       return -1;
 808:     }
 809: 
 810:     /**
 811:      * Returns the character, word or sentence at the specified index. The
 812:      * <code>part</code> parameter determines what is returned, the character,
 813:      * word or sentence after the index.
 814:      *
 815:      * @param part one of {@link AccessibleText#CHARACTER},
 816:      *             {@link AccessibleText#WORD} or
 817:      *             {@link AccessibleText#SENTENCE}, specifying what is returned
 818:      * @param index the index
 819:      *
 820:      * @return the character, word or sentence after <code>index</code>
 821:      */
 822:     public String getAtIndex(int part, int index)
 823:     {
 824:       String result = "";
 825:       int startIndex = -1;
 826:       int endIndex = -1;
 827:       switch(part)
 828:         {
 829:         case AccessibleText.CHARACTER:
 830:           result = String.valueOf(text.charAt(index));
 831:           break;
 832:         case AccessibleText.WORD:
 833:           startIndex = text.lastIndexOf(' ', index);
 834:           endIndex = text.indexOf(' ', startIndex + 1);
 835:           if (endIndex == -1)
 836:             endIndex = startIndex + 1;
 837:           result = text.substring(startIndex + 1, endIndex);
 838:           break;
 839:         case AccessibleText.SENTENCE:
 840:         default:
 841:           startIndex = text.lastIndexOf('.', index);
 842:           endIndex = text.indexOf('.', startIndex + 1);
 843:           if (endIndex == -1)
 844:             endIndex = startIndex + 1;
 845:           result = text.substring(startIndex + 1, endIndex);
 846:           break;
 847:         }
 848:       return result;
 849:     }
 850: 
 851:     /**
 852:      * Returns the character, word or sentence after the specified index. The
 853:      * <code>part</code> parameter determines what is returned, the character,
 854:      * word or sentence after the index.
 855:      *
 856:      * @param part one of {@link AccessibleText#CHARACTER},
 857:      *             {@link AccessibleText#WORD} or
 858:      *             {@link AccessibleText#SENTENCE}, specifying what is returned
 859:      * @param index the index
 860:      *
 861:      * @return the character, word or sentence after <code>index</code>
 862:      */
 863:     public String getAfterIndex(int part, int index)
 864:     {
 865:       String result = "";
 866:       int startIndex = -1;
 867:       int endIndex = -1;
 868:       switch(part)
 869:         {
 870:         case AccessibleText.CHARACTER:
 871:           result = String.valueOf(text.charAt(index + 1));
 872:           break;
 873:         case AccessibleText.WORD:
 874:           startIndex = text.indexOf(' ', index);
 875:           endIndex = text.indexOf(' ', startIndex + 1);
 876:           if (endIndex == -1)
 877:             endIndex = startIndex + 1;
 878:           result = text.substring(startIndex + 1, endIndex);
 879:           break;
 880:         case AccessibleText.SENTENCE:
 881:         default:
 882:           startIndex = text.indexOf('.', index);
 883:           endIndex = text.indexOf('.', startIndex + 1);
 884:           if (endIndex == -1)
 885:             endIndex = startIndex + 1;
 886:           result = text.substring(startIndex + 1, endIndex);
 887:           break;
 888:         }
 889:       return result;
 890:     }
 891: 
 892:     /**
 893:      * Returns the character, word or sentence before the specified index. The
 894:      * <code>part</code> parameter determines what is returned, the character,
 895:      * word or sentence before the index.
 896:      *
 897:      * @param part one of {@link AccessibleText#CHARACTER},
 898:      *             {@link AccessibleText#WORD} or
 899:      *             {@link AccessibleText#SENTENCE}, specifying what is returned
 900:      * @param index the index
 901:      *
 902:      * @return the character, word or sentence before <code>index</code>
 903:      */
 904:     public String getBeforeIndex(int part, int index)
 905:     {
 906:       String result = "";
 907:       int startIndex = -1;
 908:       int endIndex = -1;
 909:       switch(part)
 910:         {
 911:         case AccessibleText.CHARACTER:
 912:           result = String.valueOf(text.charAt(index - 1));
 913:           break;
 914:         case AccessibleText.WORD:
 915:           endIndex = text.lastIndexOf(' ', index);
 916:           if (endIndex == -1)
 917:             endIndex = 0;
 918:           startIndex = text.lastIndexOf(' ', endIndex - 1);
 919:           result = text.substring(startIndex + 1, endIndex);
 920:           break;
 921:         case AccessibleText.SENTENCE:
 922:         default:
 923:           endIndex = text.lastIndexOf('.', index);
 924:           if (endIndex == -1)
 925:             endIndex = 0;
 926:           startIndex = text.lastIndexOf('.', endIndex - 1);
 927:           result = text.substring(startIndex + 1, endIndex);
 928:           break;
 929:         }
 930:       return result;
 931:     }
 932: 
 933:     /**
 934:      * Returns the text attribute for the character at the specified character
 935:      * index.
 936:      *
 937:      * @param i the character index
 938:      *
 939:      * @return the character attributes for the specified character or
 940:      *         <code>null</code> if the character has no attributes
 941:      */
 942:     public AttributeSet getCharacterAttribute(int i)
 943:     {
 944:       AttributeSet atts = null;
 945:       View view = (View) getClientProperty(BasicHTML.propertyKey);
 946:       if (view != null)
 947:         {
 948:           Document doc = view.getDocument();
 949:           if (doc instanceof StyledDocument)
 950:             {
 951:               StyledDocument sDoc = (StyledDocument) doc;
 952:               Element charEl = sDoc.getCharacterElement(i);
 953:               if (charEl != null)
 954:                 atts = charEl.getAttributes();
 955:             }
 956:         }
 957:       return atts;
 958:     }
 959: 
 960:     /**
 961:      * This always returns <code>-1</code> since
 962:      * button labels can't be selected.
 963:      *
 964:      * @return <code>-1</code>, button labels can't be selected
 965:      */
 966:     public int getSelectionStart()
 967:     {
 968:       return -1;
 969:     }
 970: 
 971:     /**
 972:      * This always returns <code>-1</code> since
 973:      * button labels can't be selected.
 974:      *
 975:      * @return <code>-1</code>, button labels can't be selected
 976:      */
 977:     public int getSelectionEnd()
 978:     {
 979:       return -1;
 980:     }
 981: 
 982:     /**
 983:      * Returns the selected text. This always returns <code>null</code> since
 984:      * button labels can't be selected.
 985:      *
 986:      * @return <code>null</code>, button labels can't be selected
 987:      */
 988:     public String getSelectedText()
 989:     {
 990:       return null;
 991:     }
 992:   }
 993: 
 994:   /**
 995:    * Creates a new AbstractButton object. Subclasses should call the following
 996:    * sequence in their constructor in order to initialize the button correctly:
 997:    * <pre>
 998:    * super();
 999:    * init(text, icon);
1000:    * </pre>
1001:    *
1002:    * The {@link #init(String, Icon)} method is not called automatically by this
1003:    * constructor.
1004:    *
1005:    * @see #init(String, Icon)
1006:    */
1007:   public AbstractButton()
1008:   {
1009:     horizontalAlignment = CENTER;
1010:     horizontalTextPosition = TRAILING;
1011:     verticalAlignment = CENTER;
1012:     verticalTextPosition = CENTER;
1013:     borderPainted = true;
1014:     contentAreaFilled = true;
1015:     focusPainted = true;
1016:     setFocusable(true);
1017:     setAlignmentX(CENTER_ALIGNMENT);
1018:     setAlignmentY(CENTER_ALIGNMENT);
1019:     setDisplayedMnemonicIndex(-1);
1020:     setOpaque(true);
1021:     text = "";
1022:     // testing on JRE1.5 shows that the iconTextGap default value is
1023:     // hard-coded here and the 'Button.iconTextGap' setting in the
1024:     // UI defaults is ignored, at least by the MetalLookAndFeel
1025:     iconTextGap = 4;
1026:   }
1027: 
1028:   /**
1029:    * Get the model the button is currently using.
1030:    *
1031:    * @return The current model
1032:    */
1033:   public ButtonModel getModel()
1034:   {
1035:       return model;
1036:   }
1037: 
1038:   /**
1039:    * Set the model the button is currently using. This un-registers all
1040:    * listeners associated with the current model, and re-registers them
1041:    * with the new model.
1042:    *
1043:    * @param newModel The new model
1044:    */
1045:   public void setModel(ButtonModel newModel)
1046:   {
1047:     if (newModel == model)
1048:       return;
1049: 
1050:     if (model != null)
1051:       {
1052:         model.removeActionListener(actionListener);
1053:         actionListener = null;
1054:         model.removeChangeListener(changeListener);
1055:         changeListener = null;
1056:         model.removeItemListener(itemListener);
1057:         itemListener = null;
1058:       }
1059:     ButtonModel old = model;
1060:     model = newModel;
1061:     if (model != null)
1062:       {
1063:         actionListener = createActionListener();
1064:         model.addActionListener(actionListener);
1065:         changeListener = createChangeListener();
1066:         model.addChangeListener(changeListener);
1067:         itemListener = createItemListener();
1068:         model.addItemListener(itemListener);
1069:       }
1070:     firePropertyChange(MODEL_CHANGED_PROPERTY, old, model);
1071:     revalidate();
1072:     repaint();
1073:   }
1074: 
1075:  protected void init(String text, Icon icon)
1076:  {
1077:     // If text is null, we fall back to the empty
1078:     // string (which is set using AbstractButton's
1079:     // constructor).
1080:     // This way the behavior of the JDK is matched.
1081:     if(text != null)
1082:       setText(text);
1083: 
1084:     if (icon != null)
1085:       default_icon = icon;
1086: 
1087:     updateUI();
1088:  }
1089: 
1090:   /**
1091:    * <p>Returns the action command string for this button's model.</p>
1092:    *
1093:    * <p>If the action command was set to <code>null</code>, the button's
1094:    * text (label) is returned instead.</p>
1095:    *
1096:    * @return The current action command string from the button's model
1097:    */
1098:   public String getActionCommand()
1099:   {
1100:     String ac = model.getActionCommand();
1101:     if (ac != null)
1102:       return ac;
1103:     else
1104:       return text;
1105:   }
1106: 
1107:   /**
1108:    * Sets the action command string for this button's model.
1109:    *
1110:    * @param actionCommand The new action command string to set in the button's
1111:    * model.
1112:    */
1113:   public void setActionCommand(String actionCommand)
1114:   {
1115:     if (model != null)
1116:       model.setActionCommand(actionCommand);
1117:   }
1118: 
1119:   /**
1120:    * Adds an ActionListener to the button's listener list. When the
1121:    * button's model is clicked it fires an ActionEvent, and these
1122:    * listeners will be called.
1123:    *
1124:    * @param l The new listener to add
1125:    */
1126:   public void addActionListener(ActionListener l)
1127:   {
1128:     listenerList.add(ActionListener.class, l);
1129:   }
1130: 
1131:   /**
1132:    * Removes an ActionListener from the button's listener list.
1133:    *
1134:    * @param l The listener to remove
1135:    */
1136:   public void removeActionListener(ActionListener l)
1137:   {
1138:     listenerList.remove(ActionListener.class, l);
1139:   }
1140: 
1141:   /**
1142:    * Returns all added <code>ActionListener</code> objects.
1143:    *
1144:    * @return an array of listeners
1145:    *
1146:    * @since 1.4
1147:    */
1148:   public ActionListener[] getActionListeners()
1149:   {
1150:     return (ActionListener[]) listenerList.getListeners(ActionListener.class);
1151:   }
1152: 
1153:   /**
1154:    * Adds an ItemListener to the button's listener list. When the button's
1155:    * model changes state (between any of ARMED, ENABLED, PRESSED, ROLLOVER
1156:    * or SELECTED) it fires an ItemEvent, and these listeners will be
1157:    * called.
1158:    *
1159:    * @param l The new listener to add
1160:    */
1161:   public void addItemListener(ItemListener l)
1162:   {
1163:     listenerList.add(ItemListener.class, l);
1164:   }
1165: 
1166:   /**
1167:    * Removes an ItemListener from the button's listener list.
1168:    *
1169:    * @param l The listener to remove
1170:    */
1171:   public void removeItemListener(ItemListener l)
1172:   {
1173:     listenerList.remove(ItemListener.class, l);
1174:   }
1175: 
1176:   /**
1177:    * Returns all added <code>ItemListener</code> objects.
1178:    *
1179:    * @return an array of listeners
1180:    *
1181:    * @since 1.4
1182:    */
1183:   public ItemListener[] getItemListeners()
1184:   {
1185:     return (ItemListener[]) listenerList.getListeners(ItemListener.class);
1186:   }
1187: 
1188:   /**
1189:    * Adds a ChangeListener to the button's listener list. When the button's
1190:    * model changes any of its (non-bound) properties, these listeners will be
1191:    * called.
1192:    *
1193:    * @param l The new listener to add
1194:    */
1195:   public void addChangeListener(ChangeListener l)
1196:   {
1197:     listenerList.add(ChangeListener.class, l);
1198:   }
1199: 
1200:   /**
1201:    * Removes a ChangeListener from the button's listener list.
1202:    *
1203:    * @param l The listener to remove
1204:    */
1205:   public void removeChangeListener(ChangeListener l)
1206:   {
1207:     listenerList.remove(ChangeListener.class, l);
1208:   }
1209: 
1210:   /**
1211:    * Returns all added <code>ChangeListener</code> objects.
1212:    *
1213:    * @return an array of listeners
1214:    *
1215:    * @since 1.4
1216:    */
1217:   public ChangeListener[] getChangeListeners()
1218:   {
1219:     return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
1220:   }
1221: 
1222:   /**
1223:    * Calls {@link ItemListener#itemStateChanged} on each ItemListener in
1224:    * the button's listener list.
1225:    *
1226:    * @param e The event signifying that the button's model changed state
1227:    */
1228:   protected void fireItemStateChanged(ItemEvent e)
1229:   {
1230:     e.setSource(this);
1231:     ItemListener[] listeners = getItemListeners();
1232: 
1233:     for (int i = 0; i < listeners.length; i++)
1234:       listeners[i].itemStateChanged(e);
1235:   }
1236: 
1237:   /**
1238:    * Calls {@link ActionListener#actionPerformed} on each {@link
1239:    * ActionListener} in the button's listener list.
1240:    *
1241:    * @param e The event signifying that the button's model was clicked
1242:    */
1243:   protected void fireActionPerformed(ActionEvent e)
1244:   {
1245:         // Dispatch a copy of the given ActionEvent in order to
1246:         // set the source and action command correctly.
1247:     ActionEvent ae = new ActionEvent(
1248:         this,
1249:         e.getID(),
1250:         getActionCommand(),
1251:         e.getWhen(),
1252:         e.getModifiers());
1253: 
1254:     ActionListener[] listeners = getActionListeners();
1255: 
1256:     for (int i = 0; i < listeners.length; i++)
1257:       listeners[i].actionPerformed(ae);
1258:   }
1259: 
1260:   /**
1261:    * Calls {@link ChangeListener#stateChanged} on each {@link ChangeListener}
1262:    * in the button's listener list.
1263:    */
1264:   protected void fireStateChanged()
1265:   {
1266:     ChangeListener[] listeners = getChangeListeners();
1267: 
1268:     for (int i = 0; i < listeners.length; i++)
1269:       listeners[i].stateChanged(changeEvent);
1270:   }
1271: 
1272:   /**
1273:    * Get the current keyboard mnemonic value. This value corresponds to a
1274:    * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1275:    * codes) and is used to activate the button when pressed in conjunction
1276:    * with the "mouseless modifier" of the button's look and feel class, and
1277:    * when focus is in one of the button's ancestors.
1278:    *
1279:    * @return The button's current keyboard mnemonic
1280:    */
1281:   public int getMnemonic()
1282:   {
1283:     ButtonModel mod = getModel();
1284:     if (mod != null)
1285:       return mod.getMnemonic();
1286:     return -1;
1287:   }
1288: 
1289:   /**
1290:    * Set the current keyboard mnemonic value. This value corresponds to a
1291:    * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1292:    * codes) and is used to activate the button when pressed in conjunction
1293:    * with the "mouseless modifier" of the button's look and feel class, and
1294:    * when focus is in one of the button's ancestors.
1295:    *
1296:    * @param mne A new mnemonic to use for the button
1297:    */
1298:   public void setMnemonic(char mne)
1299:   {
1300:     setMnemonic((int) mne);
1301:   }
1302: 
1303:   /**
1304:    * Set the current keyboard mnemonic value. This value corresponds to a
1305:    * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1306:    * codes) and is used to activate the button when pressed in conjunction
1307:    * with the "mouseless modifier" of the button's look and feel class, and
1308:    * when focus is in one of the button's ancestors.
1309:    *
1310:    * @param mne A new mnemonic to use for the button
1311:    */
1312:   public void setMnemonic(int mne)
1313:   {
1314:     ButtonModel mod = getModel();
1315:     int old = -1;
1316:     if (mod != null)
1317:       old = mod.getMnemonic();
1318: 
1319:     if (old != mne)
1320:       {
1321:         if (mod != null)
1322:           mod.setMnemonic(mne);
1323: 
1324:         if (text != null && !text.equals(""))
1325:           {
1326:             // Since lower case char = upper case char for
1327:             // mnemonic, we will convert both text and mnemonic
1328:             // to upper case before checking if mnemonic character occurs
1329:             // in the menu item text.
1330:             int upperCaseMne = Character.toUpperCase((char) mne);
1331:             String upperCaseText = text.toUpperCase();
1332:             setDisplayedMnemonicIndex(upperCaseText.indexOf(upperCaseMne));
1333:           }
1334: 
1335:         firePropertyChange(MNEMONIC_CHANGED_PROPERTY, old, mne);
1336:         revalidate();
1337:         repaint();
1338:       }
1339:   }
1340: 
1341:   /**
1342:    * Sets the button's mnemonic index. The mnemonic index is a hint to the
1343:    * look and feel class, suggesting which character in the button's label
1344:    * should be underlined when drawing the label. If the mnemonic index is
1345:    * -1, no mnemonic will be displayed.
1346:    *
1347:    * If no mnemonic index is set, the button will choose a mnemonic index
1348:    * by default, which will be the first occurrence of the mnemonic
1349:    * character in the button's text.
1350:    *
1351:    * @param index An offset into the "text" property of the button
1352:    * @throws IllegalArgumentException If <code>index</code> is not within the
1353:    * range of legal offsets for the "text" property of the button.
1354:    * @since 1.4
1355:    */
1356: 
1357:   public void setDisplayedMnemonicIndex(int index)
1358:   {
1359:     if (index < -1 || (text != null && index >= text.length()))
1360:       throw new IllegalArgumentException();
1361: 
1362:     mnemonicIndex = index;
1363:   }
1364: 
1365:   /**
1366:    * Get the button's mnemonic index, which is an offset into the button's
1367:    * "text" property.  The character specified by this offset should be
1368:    * underlined when the look and feel class draws this button.
1369:    *
1370:    * @return An index into the button's "text" property
1371:    */
1372:   public int getDisplayedMnemonicIndex()
1373:   {
1374:     return mnemonicIndex;
1375:   }
1376: 
1377: 
1378:   /**
1379:    * Set the "rolloverEnabled" property. When rollover is enabled, and the
1380:    * look and feel supports it, the button will change its icon to
1381:    * rolloverIcon, when the mouse passes over it.
1382:    *
1383:    * @param r Whether or not to enable rollover icon changes
1384:    */
1385:   public void setRolloverEnabled(boolean r)
1386:   {
1387:     clientRolloverEnabledSet = true;
1388:     if (rollOverEnabled != r)
1389:       {
1390:         rollOverEnabled = r;
1391:         firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r);
1392:         revalidate();
1393:         repaint();
1394:       }
1395:   }
1396: 
1397:   /**
1398:    * Returns whether or not rollover icon changes are enabled on the
1399:    * button.
1400:    *
1401:    * @return The state of the "rolloverEnabled" property
1402:    */
1403:   public boolean isRolloverEnabled()
1404:   {
1405:     return rollOverEnabled;
1406:   }
1407: 
1408:   /**
1409:    * Set the value of the button's "selected" property. Selection is only
1410:    * meaningful for toggle-type buttons (check boxes, radio buttons).
1411:    *
1412:    * @param s New value for the property
1413:    */
1414:   public void setSelected(boolean s)
1415:   {
1416:     ButtonModel mod = getModel();
1417:     if (mod != null)
1418:       mod.setSelected(s);
1419:   }
1420: 
1421:   /**
1422:    * Get the value of the button's "selected" property. Selection is only
1423:    * meaningful for toggle-type buttons (check boxes, radio buttons).
1424:    *
1425:    * @return The value of the property
1426:    */
1427:   public boolean isSelected()
1428:   {
1429:     ButtonModel mod = getModel();
1430:     if (mod != null)
1431:       return mod.isSelected();
1432:     return false;
1433:   }
1434: 
1435:   /**
1436:    * Enables or disables the button. A button will neither be selectable
1437:    * nor preform any actions unless it is enabled.
1438:    *
1439:    * @param b Whether or not to enable the button
1440:    */
1441:   public void setEnabled(boolean b)
1442:   {
1443:     // Do nothing if state does not change.
1444:     if (b == isEnabled())
1445:       return;
1446:     super.setEnabled(b);
1447:     setFocusable(b);
1448:     ButtonModel mod = getModel();
1449:     if (mod != null)
1450:       mod.setEnabled(b);
1451:   }
1452: 
1453:   /**
1454:    * Set the horizontal alignment of the button's text and icon. The
1455:    * alignment is a numeric constant from {@link SwingConstants}. It must
1456:    * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1457:    * <code>LEADING</code> or <code>TRAILING</code>.  The default is
1458:    * <code>CENTER</code>.
1459:    *
1460:    * @return The current horizontal alignment
1461:    *
1462:    * @see #setHorizontalAlignment(int)
1463:    */
1464:   public int getHorizontalAlignment()
1465:   {
1466:     return horizontalAlignment;
1467:   }
1468: 
1469:   /**
1470:    * Set the horizontal alignment of the button's text and icon. The
1471:    * alignment is a numeric constant from {@link SwingConstants}. It must
1472:    * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1473:    * <code>LEADING</code> or <code>TRAILING</code>.  The default is
1474:    * <code>CENTER</code>.
1475:    *
1476:    * @param a The new horizontal alignment
1477:    * @throws IllegalArgumentException If alignment is not one of the legal
1478:    * constants.
1479:    *
1480:    * @see #getHorizontalAlignment()
1481:    */
1482:   public void setHorizontalAlignment(int a)
1483:   {
1484:     if (horizontalAlignment == a)
1485:       return;
1486:     if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING
1487:         && a != TRAILING)
1488:       throw new IllegalArgumentException("Invalid alignment.");
1489:     int old = horizontalAlignment;
1490:     horizontalAlignment = a;
1491:     firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1492:     revalidate();
1493:     repaint();
1494:   }
1495: 
1496:   /**
1497:    * Get the horizontal position of the button's text relative to its
1498:    * icon. The position is a numeric constant from {@link
1499:    * SwingConstants}. It must be one of: <code>RIGHT</code>,
1500:    * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1501:    * <code>TRAILING</code>.  The default is <code>TRAILING</code>.
1502:    *
1503:    * @return The current horizontal text position
1504:    */
1505:   public int getHorizontalTextPosition()
1506:   {
1507:     return horizontalTextPosition;
1508:   }
1509: 
1510:   /**
1511:    * Set the horizontal position of the button's text relative to its
1512:    * icon. The position is a numeric constant from {@link
1513:    * SwingConstants}. It must be one of: <code>RIGHT</code>,
1514:    * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1515:    * <code>TRAILING</code>. The default is <code>TRAILING</code>.
1516:    *
1517:    * @param t The new horizontal text position
1518:    * @throws IllegalArgumentException If position is not one of the legal
1519:    * constants.
1520:    */
1521:   public void setHorizontalTextPosition(int t)
1522:   {
1523:     if (horizontalTextPosition == t)
1524:       return;
1525:     if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING
1526:         && t != TRAILING)
1527:       throw new IllegalArgumentException("Invalid alignment.");
1528: 
1529:     int old = horizontalTextPosition;
1530:     horizontalTextPosition = t;
1531:     firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1532:     revalidate();
1533:     repaint();
1534:   }
1535: 
1536:   /**
1537:    * Get the vertical alignment of the button's text and icon. The
1538:    * alignment is a numeric constant from {@link SwingConstants}. It must
1539:    * be one of: <code>CENTER</code>, <code>TOP</code>, or
1540:    * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1541:    *
1542:    * @return The current vertical alignment
1543:    *
1544:    * @see #setVerticalAlignment(int)
1545:    */
1546:   public int getVerticalAlignment()
1547:   {
1548:     return verticalAlignment;
1549:   }
1550: 
1551:   /**
1552:    * Set the vertical alignment of the button's text and icon. The
1553:    * alignment is a numeric constant from {@link SwingConstants}. It must
1554:    * be one of: <code>CENTER</code>, <code>TOP</code>, or
1555:    * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1556:    *
1557:    * @param a The new vertical alignment
1558:    * @throws IllegalArgumentException If alignment is not one of the legal
1559:    * constants.
1560:    *
1561:    * @see #getVerticalAlignment()
1562:    */
1563:   public void setVerticalAlignment(int a)
1564:   {
1565:     if (verticalAlignment == a)
1566:       return;
1567:     if (a != TOP && a != CENTER && a != BOTTOM)
1568:       throw new IllegalArgumentException("Invalid alignment.");
1569: 
1570:     int old = verticalAlignment;
1571:     verticalAlignment = a;
1572:     firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1573:     revalidate();
1574:     repaint();
1575:   }
1576: 
1577:   /**
1578:    * Get the vertical position of the button's text relative to its
1579:    * icon. The alignment is a numeric constant from {@link
1580:    * SwingConstants}. It must be one of: <code>CENTER</code>,
1581:    * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1582:    * <code>CENTER</code>.
1583:    *
1584:    * @return The current vertical position
1585:    */
1586:   public int getVerticalTextPosition()
1587:   {
1588:     return verticalTextPosition;
1589:   }
1590: 
1591:   /**
1592:    * Set the vertical position of the button's text relative to its
1593:    * icon. The alignment is a numeric constant from {@link
1594:    * SwingConstants}. It must be one of: <code>CENTER</code>,
1595:    * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1596:    * <code>CENTER</code>.
1597:    *
1598:    * @param t The new vertical position
1599:    * @throws IllegalArgumentException If position is not one of the legal
1600:    * constants.
1601:    */
1602:   public void setVerticalTextPosition(int t)
1603:   {
1604:     if (verticalTextPosition == t)
1605:       return;
1606:     if (t != TOP && t != CENTER && t != BOTTOM)
1607:       throw new IllegalArgumentException("Invalid alignment.");
1608: 
1609:     int old = verticalTextPosition;
1610:     verticalTextPosition = t;
1611:     firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1612:     revalidate();
1613:     repaint();
1614:   }
1615: 
1616:   /**
1617:    * Set the value of the "borderPainted" property. If set to
1618:    * <code>false</code>, the button's look and feel class should not paint
1619:    * a border for the button. The default is <code>true</code>.
1620:    *
1621:    * @return The current value of the property.
1622:    */
1623:   public boolean isBorderPainted()
1624:   {
1625:     return borderPainted;
1626:   }
1627: 
1628:   /**
1629:    * Set the value of the "borderPainted" property. If set to
1630:    * <code>false</code>, the button's look and feel class should not paint
1631:    * a border for the button. The default is <code>true</code>.
1632:    *
1633:    * @param b The new value of the property.
1634:    */
1635:   public void setBorderPainted(boolean b)
1636:   {
1637:     clientBorderPaintedSet = true;
1638:     if (borderPainted == b)
1639:       return;
1640:     boolean old = borderPainted;
1641:     borderPainted = b;
1642:     firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
1643:     revalidate();
1644:     repaint();
1645:   }
1646: 
1647:   /**
1648:    * Get the value of the "action" property.
1649:    *
1650:    * @return The current value of the "action" property
1651:    */
1652:   public Action getAction()
1653:   {
1654:     return action;
1655:   }
1656: 
1657:   /**
1658:    * <p>Set the button's "action" property, subscribing the new action to the
1659:    * button, as an ActionListener, if it is not already subscribed. The old
1660:    * Action, if it exists, is unsubscribed, and the button is unsubscribed
1661:    * from the old Action if it was previously subscribed as a
1662:    * PropertyChangeListener.</p>
1663:    *
1664:    * <p>This method also configures several of the button's properties from
1665:    * the Action, by calling {@link #configurePropertiesFromAction}, and
1666:    * subscribes the button to the Action as a PropertyChangeListener.
1667:    * Subsequent changes to the Action will thus reconfigure the button
1668:    * automatically.</p>
1669:    *
1670:    * @param a The new value of the "action" property
1671:    */
1672:   public void setAction(Action a)
1673:   {
1674:     if (action != null)
1675:       {
1676:         action.removePropertyChangeListener(actionPropertyChangeListener);
1677:         removeActionListener(action);
1678:         if (actionPropertyChangeListener != null)
1679:           {
1680:             action.removePropertyChangeListener(actionPropertyChangeListener);
1681:             actionPropertyChangeListener = null;
1682:           }
1683:       }
1684: 
1685:     Action old = action;
1686:     action = a;
1687:     configurePropertiesFromAction(action);
1688:     if (action != null)
1689:       {
1690:         actionPropertyChangeListener = createActionPropertyChangeListener(a);
1691:         action.addPropertyChangeListener(actionPropertyChangeListener);
1692:         addActionListener(action);
1693:       }
1694:   }
1695: 
1696:   /**
1697:    * Return the button's default "icon" property.
1698:    *
1699:    * @return The current default icon
1700:    */
1701:   public Icon getIcon()
1702:   {
1703:     return default_icon;
1704:   }
1705: 
1706:   /**
1707:    * Set the button's default "icon" property. This icon is used as a basis
1708:    * for the pressed and disabled icons, if none are explicitly set.
1709:    *
1710:    * @param i The new default icon
1711:    */
1712:   public void setIcon(Icon i)
1713:   {
1714:     if (default_icon == i)
1715:       return;
1716: 
1717:     Icon old = default_icon;
1718:     default_icon = i;
1719:     firePropertyChange(ICON_CHANGED_PROPERTY, old, i);
1720:     revalidate();
1721:     repaint();
1722:   }
1723: 
1724:   /**
1725:    * Return the button's "text" property. This property is synonymous with
1726:    * the "label" property.
1727:    *
1728:    * @return The current "text" property
1729:    */
1730:   public String getText()
1731:   {
1732:     return text;
1733:   }
1734: 
1735:   /**
1736:    * Set the button's "label" property. This property is synonymous with the
1737:    * "text" property.
1738:    *
1739:    * @param label The new "label" property
1740:    *
1741:    * @deprecated use <code>setText(text)</code>
1742:    */
1743:   public void setLabel(String label)
1744:   {
1745:     setText(label);
1746:   }
1747: 
1748:   /**
1749:    * Return the button's "label" property. This property is synonymous with
1750:    * the "text" property.
1751:    *
1752:    * @return The current "label" property
1753:    *
1754:    * @deprecated use <code>getText()</code>
1755:    */
1756:   public String getLabel()
1757:   {
1758:     return getText();
1759:   }
1760: 
1761:   /**
1762:    * Set the button's "text" property. This property is synonymous with the
1763:    * "label" property.
1764:    *
1765:    * @param t The new "text" property
1766:    */
1767:   public void setText(String t)
1768:   {
1769:     if (text == t)
1770:       return;
1771: 
1772:     String old = text;
1773:     text = t;
1774:     firePropertyChange(TEXT_CHANGED_PROPERTY, old, t);
1775:     revalidate();
1776:     repaint();
1777:   }
1778: 
1779:   /**
1780:    * Set the value of the {@link #iconTextGap} property.
1781:    *
1782:    * @param i The new value of the property
1783:    *
1784:    * @since 1.4
1785:    */
1786:   public void setIconTextGap(int i)
1787:   {
1788:     clientIconTextGapSet = true;
1789:     if (iconTextGap == i)
1790:       return;
1791: 
1792:     int old = iconTextGap;
1793:     iconTextGap = i;
1794:     firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
1795:     revalidate();
1796:     repaint();
1797:   }
1798: 
1799:   /**
1800:    * Get the value of the {@link #iconTextGap} property.
1801:    *
1802:    * @return The current value of the property
1803:    *
1804:    * @since 1.4
1805:    */
1806:   public int getIconTextGap()
1807:   {
1808:     return iconTextGap;
1809:   }
1810: 
1811:   /**
1812:    * Return the button's "margin" property, which is an {@link Insets} object
1813:    * describing the distance between the button's border and its text and
1814:    * icon.
1815:    *
1816:    * @return The current "margin" property
1817:    */
1818:   public Insets getMargin()
1819:   {
1820:     return margin;
1821:   }
1822: 
1823:   /**
1824:    * Set the button's "margin" property, which is an {@link Insets} object
1825:    * describing the distance between the button's border and its text and
1826:    * icon.
1827:    *
1828:    * @param m The new "margin" property
1829:    */
1830:   public void setMargin(Insets m)
1831:   {
1832:     if (margin == m)
1833:       return;
1834: 
1835:     Insets old = margin;
1836:     margin = m;
1837:     firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
1838:     revalidate();
1839:     repaint();
1840:   }
1841: 
1842:   /**
1843:    * Return the button's "pressedIcon" property. The look and feel class
1844:    * should paint this icon when the "pressed" property of the button's
1845:    * {@link ButtonModel} is <code>true</code>. This property may be
1846:    * <code>null</code>, in which case the default icon is used.
1847:    *
1848:    * @return The current "pressedIcon" property
1849:    */
1850:   public Icon getPressedIcon()
1851:   {
1852:     return pressed_icon;
1853:   }
1854: 
1855:   /**
1856:    * Set the button's "pressedIcon" property. The look and feel class
1857:    * should paint this icon when the "pressed" property of the button's
1858:    * {@link ButtonModel} is <code>true</code>. This property may be
1859:    * <code>null</code>, in which case the default icon is used.
1860:    *
1861:    * @param pressedIcon The new "pressedIcon" property
1862:    */
1863:   public void setPressedIcon(Icon pressedIcon)
1864:   {
1865:     if (pressed_icon == pressedIcon)
1866:       return;
1867: 
1868:     Icon old = pressed_icon;
1869:     pressed_icon = pressedIcon;
1870:     firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon);
1871:     revalidate();
1872:     repaint();
1873:   }
1874: 
1875:   /**
1876:    * Return the button's "disabledIcon" property. The look and feel class
1877:    * should paint this icon when the "enabled" property of the button's
1878:    * {@link ButtonModel} is <code>false</code>. This property may be
1879:    * <code>null</code>, in which case an icon is constructed, based on the
1880:    * default icon.
1881:    *
1882:    * @return The current "disabledIcon" property
1883:    */
1884:   public Icon getDisabledIcon()
1885:   {
1886:     if (disabledIcon == null && default_icon instanceof ImageIcon)
1887:       {
1888:         Image iconImage = ((ImageIcon) default_icon).getImage();
1889:         Image grayImage = GrayFilter.createDisabledImage(iconImage);
1890:         disabledIcon = new ImageIcon(grayImage);
1891:       }
1892: 
1893:     return disabledIcon;
1894:   }
1895: 
1896:   /**
1897:    * Set the button's "disabledIcon" property. The look and feel class should
1898:    * paint this icon when the "enabled" property of the button's {@link
1899:    * ButtonModel} is <code>false</code>. This property may be
1900:    * <code>null</code>, in which case an icon is constructed, based on the
1901:    * default icon.
1902:    *
1903:    * @param d The new "disabledIcon" property
1904:    */
1905:   public void setDisabledIcon(Icon d)
1906:   {
1907:     if (disabledIcon == d)
1908:       return;
1909:     Icon old = disabledIcon;
1910:     disabledIcon = d;
1911:     firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d);
1912:     revalidate();
1913:     repaint();
1914:   }
1915: 
1916:   /**
1917:    * Return the button's "paintFocus" property. This property controls
1918:    * whether or not the look and feel class will paint a special indicator
1919:    * of focus state for the button. If it is false, the button still paints
1920:    * when focused, but no special decoration is painted to indicate the
1921:    * presence of focus.
1922:    *
1923:    * @return The current "paintFocus" property
1924:    */
1925:   public boolean isFocusPainted()
1926:   {
1927:     return focusPainted;
1928:   }
1929: 
1930:   /**
1931:    * Set the button's "paintFocus" property. This property controls whether
1932:    * or not the look and feel class will paint a special indicator of focus
1933:    * state for the button. If it is false, the button still paints when
1934:    * focused, but no special decoration is painted to indicate the presence
1935:    * of focus.
1936:    *
1937:    * @param p The new "paintFocus" property
1938:    */
1939:   public void setFocusPainted(boolean p)
1940:   {
1941:     if (focusPainted == p)
1942:       return;
1943: 
1944:     boolean old = focusPainted;
1945:     focusPainted = p;
1946:     firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p);
1947:     revalidate();
1948:     repaint();
1949:   }
1950: 
1951:   /**
1952:    * Verifies that a particular key is one of the valid constants used for
1953:    * describing horizontal alignment and positioning. The valid constants
1954:    * are the following members of {@link SwingConstants}:
1955:    * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1956:    * <code>LEADING</code> or <code>TRAILING</code>.
1957:    *
1958:    * @param key The key to check
1959:    * @param exception A message to include in an IllegalArgumentException
1960:    *
1961:    * @return the value of key
1962:    *
1963:    * @throws IllegalArgumentException If key is not one of the valid constants
1964:    *
1965:    * @see #setHorizontalTextPosition(int)
1966:    * @see #setHorizontalAlignment(int)
1967:    */
1968:   protected  int checkHorizontalKey(int key, String exception)
1969:   {
1970:     switch (key)
1971:       {
1972:       case SwingConstants.RIGHT:
1973:       case SwingConstants.LEFT:
1974:       case SwingConstants.CENTER:
1975:       case SwingConstants.LEADING:
1976:       case SwingConstants.TRAILING:
1977:         break;
1978:       default:
1979:         throw new IllegalArgumentException(exception);
1980:       }
1981:     return key;
1982:   }
1983: 
1984:   /**
1985:    * Verifies that a particular key is one of the valid constants used for
1986:    * describing vertical alignment and positioning. The valid constants are
1987:    * the following members of {@link SwingConstants}: <code>TOP</code>,
1988:    * <code>BOTTOM</code> or <code>CENTER</code>.
1989:    *
1990:    * @param key The key to check
1991:    * @param exception A message to include in an IllegalArgumentException
1992:    *
1993:    * @return the value of key
1994:    *
1995:    * @throws IllegalArgumentException If key is not one of the valid constants
1996:    *
1997:    * @see #setVerticalTextPosition(int)
1998:    * @see #setVerticalAlignment(int)
1999:    */
2000:   protected  int checkVerticalKey(int key, String exception)
2001:   {
2002:     switch (key)
2003:       {
2004:       case SwingConstants.TOP:
2005:       case SwingConstants.BOTTOM:
2006:       case SwingConstants.CENTER:
2007:         break;
2008:       default:
2009:         throw new IllegalArgumentException(exception);
2010:       }
2011:     return key;
2012:   }
2013: 
2014:   /**
2015:    * Configure various properties of the button by reading properties
2016:    * of an {@link Action}. The mapping of properties is as follows:
2017:    *
2018:    * <table>
2019:    *
2020:    * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr>
2021:    *
2022:    * <tr><td>NAME                 </td> <td>text                   </td></tr>
2023:    * <tr><td>SMALL_ICON           </td> <td>icon                   </td></tr>
2024:    * <tr><td>SHORT_DESCRIPTION    </td> <td>toolTipText            </td></tr>
2025:    * <tr><td>MNEMONIC_KEY         </td> <td>mnemonic               </td></tr>
2026:    * <tr><td>ACTION_COMMAND_KEY   </td> <td>actionCommand          </td></tr>
2027:    *
2028:    * </table>
2029:    *
2030:    * <p>In addition, this method always sets the button's "enabled" property to
2031:    * the value of the Action's "enabled" property.</p>
2032:    *
2033:    * <p>If the provided Action is <code>null</code>, the text, icon, and
2034:    * toolTipText properties of the button are set to <code>null</code>, and
2035:    * the "enabled" property is set to <code>true</code>; the mnemonic and
2036:    * actionCommand properties are unchanged.</p>
2037:    *
2038:    * @param a An Action to configure the button from
2039:    */
2040:   protected void configurePropertiesFromAction(Action a)
2041:   {
2042:     if (a == null)
2043:       {
2044:         setText(null);
2045:         setIcon(null);
2046:         setEnabled(true);
2047:         setToolTipText(null);
2048:       }
2049:     else
2050:       {
2051:         setText((String) (a.getValue(Action.NAME)));
2052:         setIcon((Icon) (a.getValue(Action.SMALL_ICON)));
2053:         setEnabled(a.isEnabled());
2054:         setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
2055:         if (a.getValue(Action.MNEMONIC_KEY) != null)
2056:           setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue());
2057:         String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY));
2058: 
2059:         // Set actionCommand to button's text by default if it is not specified
2060:         if (actionCommand != null)
2061:           setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY)));
2062:         else
2063:           setActionCommand(getText());
2064:       }
2065:   }
2066: 
2067:   /**
2068:    * <p>A factory method which should return an {@link ActionListener} that
2069:    * propagates events from the button's {@link ButtonModel} to any of the
2070:    * button's ActionListeners. By default, this is an inner class which
2071:    * calls {@link AbstractButton#fireActionPerformed} with a modified copy
2072:    * of the incoming model {@link ActionEvent}.</p>
2073:    *
2074:    * <p>The button calls this method during construction, stores the
2075:    * resulting ActionListener in its <code>actionListener</code> member
2076:    * field, and subscribes it to the button's model. If the button's model
2077:    * is changed, this listener is unsubscribed from the old model and
2078:    * subscribed to the new one.</p>
2079:    *
2080:    * @return A new ActionListener
2081:    */
2082:   protected  ActionListener createActionListener()
2083:   {
2084:     return getEventHandler();
2085:   }
2086: 
2087:   /**
2088:    * <p>A factory method which should return a {@link PropertyChangeListener}
2089:    * that accepts changes to the specified {@link Action} and reconfigure
2090:    * the {@link AbstractButton}, by default using the {@link
2091:    * #configurePropertiesFromAction} method.</p>
2092:    *
2093:    * <p>The button calls this method whenever a new Action is assigned to
2094:    * the button's "action" property, via {@link #setAction}, and stores the
2095:    * resulting PropertyChangeListener in its
2096:    * <code>actionPropertyChangeListener</code> member field. The button
2097:    * then subscribes the listener to the button's new action. If the
2098:    * button's action is changed subsequently, the listener is unsubscribed
2099:    * from the old action and subscribed to the new one.</p>
2100:    *
2101:    * @param a The Action which will be listened to, and which should be
2102:    * the same as the source of any PropertyChangeEvents received by the
2103:    * new listener returned from this method.
2104:    *
2105:    * @return A new PropertyChangeListener
2106:    */
2107:   protected  PropertyChangeListener createActionPropertyChangeListener(Action a)
2108:   {
2109:     return new PropertyChangeListener()
2110:       {
2111:         public void propertyChange(PropertyChangeEvent e)
2112:         {
2113:           Action act = (Action) (e.getSource());
2114:           if (e.getPropertyName().equals("enabled"))
2115:             setEnabled(act.isEnabled());
2116:           else if (e.getPropertyName().equals(Action.NAME))
2117:             setText((String) (act.getValue(Action.NAME)));
2118:           else if (e.getPropertyName().equals(Action.SMALL_ICON))
2119:             setIcon((Icon) (act.getValue(Action.SMALL_ICON)));
2120:           else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION))
2121:             setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION)));
2122:           else if (e.getPropertyName().equals(Action.MNEMONIC_KEY))
2123:             if (act.getValue(Action.MNEMONIC_KEY) != null)
2124:               setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY)))
2125:                           .intValue());
2126:           else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY))
2127:             setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY)));
2128:         }
2129:       };
2130:   }
2131: 
2132:   /**
2133:    * <p>Factory method which creates a {@link ChangeListener}, used to
2134:    * subscribe to ChangeEvents from the button's model. Subclasses of
2135:    * AbstractButton may wish to override the listener used to subscribe to
2136:    * such ChangeEvents. By default, the listener just propagates the
2137:    * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
2138:    * AbstractButton#fireStateChanged} method.</p>
2139:    *
2140:    * <p>The button calls this method during construction, stores the
2141:    * resulting ChangeListener in its <code>changeListener</code> member
2142:    * field, and subscribes it to the button's model. If the button's model
2143:    * is changed, this listener is unsubscribed from the old model and
2144:    * subscribed to the new one.</p>
2145:    *
2146:    * @return The new ChangeListener
2147:    */
2148:   protected ChangeListener createChangeListener()
2149:   {
2150:     return getEventHandler();
2151:   }
2152: 
2153:   /**
2154:    * <p>Factory method which creates a {@link ItemListener}, used to
2155:    * subscribe to ItemEvents from the button's model. Subclasses of
2156:    * AbstractButton may wish to override the listener used to subscribe to
2157:    * such ItemEvents. By default, the listener just propagates the
2158:    * {@link ItemEvent} to the button's ItemListeners, via the {@link
2159:    * AbstractButton#fireItemStateChanged} method.</p>
2160:    *
2161:    * <p>The button calls this method during construction, stores the
2162:    * resulting ItemListener in its <code>changeListener</code> member
2163:    * field, and subscribes it to the button's model. If the button's model
2164:    * is changed, this listener is unsubscribed from the old model and
2165:    * subscribed to the new one.</p>
2166:    *
2167:    * <p>Note that ItemEvents are only generated from the button's model
2168:    * when the model's <em>selected</em> property changes. If you want to
2169:    * subscribe to other properties of the model, you must subscribe to
2170:    * ChangeEvents.
2171:    *
2172:    * @return The new ItemListener
2173:    */
2174:   protected  ItemListener createItemListener()
2175:   {
2176:     return getEventHandler();
2177:   }
2178: 
2179:   /**
2180:    * Programmatically perform a "click" on the button: arming, pressing,
2181:    * waiting, un-pressing, and disarming the model.
2182:    */
2183:   public void doClick()
2184:   {
2185:     doClick(100);
2186:   }
2187: 
2188:   /**
2189:    * Programmatically perform a "click" on the button: arming, pressing,
2190:    * waiting, un-pressing, and disarming the model.
2191:    *
2192:    * @param pressTime The number of milliseconds to wait in the pressed state
2193:    */
2194:   public void doClick(int pressTime)
2195:   {
2196:     ButtonModel mod = getModel();
2197:     if (mod != null)
2198:       {
2199:         mod.setArmed(true);
2200:         mod.setPressed(true);
2201:         try
2202:           {
2203:             java.lang.Thread.sleep(pressTime);
2204:           }
2205:         catch (java.lang.InterruptedException e)
2206:           {
2207:             // probably harmless
2208:           }
2209:         mod.setPressed(false);
2210:         mod.setArmed(false);
2211:       }
2212:   }
2213: 
2214:   /**
2215:    * Return the button's disabled selected icon. The look and feel class
2216:    * should paint this icon when the "enabled" property of the button's model
2217:    * is <code>false</code> and its "selected" property is
2218:    * <code>true</code>. This icon can be <code>null</code>, in which case
2219:    * it is synthesized from the button's selected icon.
2220:    *
2221:    * @return The current disabled selected icon
2222:    */
2223:   public Icon getDisabledSelectedIcon()
2224:   {
2225:     return disabledSelectedIcon;
2226:   }
2227: 
2228:   /**
2229:    * Set the button's disabled selected icon. The look and feel class
2230:    * should paint this icon when the "enabled" property of the button's model
2231:    * is <code>false</code> and its "selected" property is
2232:    * <code>true</code>. This icon can be <code>null</code>, in which case
2233:    * it is synthesized from the button's selected icon.
2234:    *
2235:    * @param icon The new disabled selected icon
2236:    */
2237:   public void setDisabledSelectedIcon(Icon icon)
2238:   {
2239:     if (disabledSelectedIcon == icon)
2240:       return;
2241: 
2242:     Icon old = disabledSelectedIcon;
2243:     disabledSelectedIcon = icon;
2244:     firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
2245:     revalidate();
2246:     repaint();
2247:   }
2248: 
2249:   /**
2250:    * Return the button's rollover icon. The look and feel class should
2251:    * paint this icon when the "rolloverEnabled" property of the button is
2252:    * <code>true</code> and the mouse rolls over the button.
2253:    *
2254:    * @return The current rollover icon
2255:    */
2256:   public Icon getRolloverIcon()
2257:   {
2258:     return rolloverIcon;
2259:   }
2260: 
2261:   /**
2262:    * Set the button's rollover icon and sets the <code>rolloverEnabled</code>
2263:    * property to <code>true</code>. The look and feel class should
2264:    * paint this icon when the "rolloverEnabled" property of the button is
2265:    * <code>true</code> and the mouse rolls over the button.
2266:    *
2267:    * @param r The new rollover icon
2268:    */
2269:   public void setRolloverIcon(Icon r)
2270:   {
2271:     if (rolloverIcon == r)
2272:       return;
2273: 
2274:     Icon old = rolloverIcon;
2275:     rolloverIcon = r;
2276:     firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
2277:     setRolloverEnabled(true);
2278:     revalidate();
2279:     repaint();
2280:   }
2281: 
2282:   /**
2283:    * Return the button's rollover selected icon. The look and feel class
2284:    * should paint this icon when the "rolloverEnabled" property of the button
2285:    * is <code>true</code>, the "selected" property of the button's model is
2286:    * <code>true</code>, and the mouse rolls over the button.
2287:    *
2288:    * @return The current rollover selected icon
2289:    */
2290:   public Icon getRolloverSelectedIcon()
2291:   {
2292:     return rolloverSelectedIcon;
2293:   }
2294: 
2295:   /**
2296:    * Set the button's rollover selected icon and sets the
2297:    * <code>rolloverEnabled</code> property to <code>true</code>. The look and
2298:    * feel class should paint this icon when the "rolloverEnabled" property of
2299:    * the button is <code>true</code>, the "selected" property of the button's
2300:    * model is <code>true</code>, and the mouse rolls over the button.
2301:    *
2302:    * @param r The new rollover selected icon.
2303:    */
2304:   public void setRolloverSelectedIcon(Icon r)
2305:   {
2306:     if (rolloverSelectedIcon == r)
2307:       return;
2308: 
2309:     Icon old = rolloverSelectedIcon;
2310:     rolloverSelectedIcon = r;
2311:     firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
2312:     setRolloverEnabled(true);
2313:     revalidate();
2314:     repaint();
2315:   }
2316: 
2317:   /**
2318:    * Return the button's selected icon. The look and feel class should
2319:    * paint this icon when the "selected" property of the button's model is
2320:    * <code>true</code>, and either the "rolloverEnabled" property of the
2321:    * button is <code>false</code> or the mouse is not currently rolled
2322:    * over the button.
2323:    *
2324:    * @return The current selected icon
2325:    */
2326:   public Icon getSelectedIcon()
2327:   {
2328:     return selectedIcon;
2329:   }
2330: 
2331:   /**
2332:    * Set the button's selected icon. The look and feel class should
2333:    * paint this icon when the "selected" property of the button's model is
2334:    * <code>true</code>, and either the "rolloverEnabled" property of the
2335:    * button is <code>false</code> or the mouse is not currently rolled
2336:    * over the button.
2337:    *
2338:    * @param s The new selected icon
2339:    */
2340:   public void setSelectedIcon(Icon s)
2341:   {
2342:     if (selectedIcon == s)
2343:       return;
2344: 
2345:     Icon old = selectedIcon;
2346:     selectedIcon = s;
2347:     firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
2348:     revalidate();
2349:     repaint();
2350:   }
2351: 
2352:   /**
2353:    * Returns an single-element array containing the "text" property of the
2354:    * button if the "selected" property of the button's model is
2355:    * <code>true</code>, otherwise returns <code>null</code>.
2356:    *
2357:    * @return The button's "selected object" array
2358:    */
2359:   public Object[] getSelectedObjects()
2360:   {
2361:     if (isSelected())
2362:       {
2363:         Object[] objs = new Object[1];
2364:         objs[0] = getText();
2365:         return objs;
2366:       }
2367:     else
2368:       {
2369:         return null;
2370:       }
2371:   }
2372: 
2373:   /**
2374:    * Called when image data becomes available for one of the button's icons.
2375:    *
2376:    * @param img The image being updated
2377:    * @param infoflags One of the constant codes in {@link ImageObserver} used
2378:    *     to describe updated portions of an image.
2379:    * @param x X coordinate of the region being updated
2380:    * @param y Y coordinate of the region being updated
2381:    * @param w Width of the region beign updated
2382:    * @param h Height of the region being updated
2383:    *
2384:    * @return <code>true</code> if img is equal to the button's current icon,
2385:    *     otherwise <code>false</code>
2386:    */
2387:   public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
2388:                              int h)
2389:   {
2390:     return current_icon == img;
2391:   }
2392: 
2393:   /**
2394:    * Returns the value of the button's "contentAreaFilled" property. This
2395:    * property indicates whether the area surrounding the text and icon of
2396:    * the button should be filled by the look and feel class.  If this
2397:    * property is <code>false</code>, the look and feel class should leave
2398:    * the content area transparent.
2399:    *
2400:    * @return The current value of the "contentAreaFilled" property
2401:    */
2402:   public boolean isContentAreaFilled()
2403:   {
2404:     return contentAreaFilled;
2405:   }
2406: 
2407:   /**
2408:    * Sets the value of the button's "contentAreaFilled" property. This
2409:    * property indicates whether the area surrounding the text and icon of
2410:    * the button should be filled by the look and feel class.  If this
2411:    * property is <code>false</code>, the look and feel class should leave
2412:    * the content area transparent.
2413:    *
2414:    * @param b The new value of the "contentAreaFilled" property
2415:    */
2416:   public void setContentAreaFilled(boolean b)
2417:   {
2418:     clientContentAreaFilledSet = true;
2419:     if (contentAreaFilled == b)
2420:       return;
2421: 
2422:     // The JDK sets the opaque property to the value of the contentAreaFilled
2423:     // property, so should we do.
2424:     setOpaque(b);
2425:     boolean old = contentAreaFilled;
2426:     contentAreaFilled = b;
2427:     firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
2428:    }
2429: 
2430:   /**
2431:    * Paints the button's border, if the button's "borderPainted" property is
2432:    * <code>true</code>, by out calling to the button's look and feel class.
2433:    *
2434:    * @param g The graphics context used to paint the border
2435:    */
2436:   protected void paintBorder(Graphics g)
2437:   {
2438:     if (isBorderPainted())
2439:       super.paintBorder(g);
2440:   }
2441: 
2442:   /**
2443:    * Returns a string, used only for debugging, which identifies or somehow
2444:    * represents this button. The exact value is implementation-defined.
2445:    *
2446:    * @return A string representation of the button
2447:    */
2448:   protected String paramString()
2449:   {
2450:     CPStringBuilder sb = new CPStringBuilder();
2451:     sb.append(super.paramString());
2452:     sb.append(",defaultIcon=");
2453:     if (getIcon() != null)
2454:       sb.append(getIcon());
2455:     sb.append(",disabledIcon=");
2456:     if (getDisabledIcon() != null)
2457:       sb.append(getDisabledIcon());
2458:     sb.append(",disabledSelectedIcon=");
2459:     if (getDisabledSelectedIcon() != null)
2460:       sb.append(getDisabledSelectedIcon());
2461:     sb.append(",margin=");
2462:     if (getMargin() != null)
2463:       sb.append(getMargin());
2464:     sb.append(",paintBorder=").append(isBorderPainted());
2465:     sb.append(",paintFocus=").append(isFocusPainted());
2466:     sb.append(",pressedIcon=");
2467:     if (getPressedIcon() != null)
2468:       sb.append(getPressedIcon());
2469:     sb.append(",rolloverEnabled=").append(isRolloverEnabled());
2470:     sb.append(",rolloverIcon=");
2471:     if (getRolloverIcon() != null)
2472:       sb.append(getRolloverIcon());
2473:     sb.append(",rolloverSelected=");
2474:     if (getRolloverSelectedIcon() != null)
2475:       sb.append(getRolloverSelectedIcon());
2476:     sb.append(",selectedIcon=");
2477:     if (getSelectedIcon() != null)
2478:       sb.append(getSelectedIcon());
2479:     sb.append(",text=");
2480:     if (getText() != null)
2481:       sb.append(getText());
2482:     return sb.toString();
2483:   }
2484: 
2485:   /**
2486:    * Set the "UI" property of the button, which is a look and feel class
2487:    * responsible for handling the button's input events and painting it.
2488:    *
2489:    * @param ui The new "UI" property
2490:    */
2491:   public void setUI(ButtonUI ui)
2492:   {
2493:     super.setUI(ui);
2494:   }
2495: 
2496:   /**
2497:    * Set the "UI" property of the button, which is a look and feel class
2498:    * responsible for handling the button's input events and painting it.
2499:    *
2500:    * @return The current "UI" property
2501:    */
2502:   public ButtonUI getUI()
2503:   {
2504:     return (ButtonUI) ui;
2505:   }
2506: 
2507:   /**
2508:    * Set the "UI" property to a class constructed, via the {@link
2509:    * UIManager}, from the current look and feel. This should be overridden
2510:    * for each subclass of AbstractButton, to retrieve a suitable {@link
2511:    * ButtonUI} look and feel class.
2512:    */
2513:   public void updateUI()
2514:   {
2515:     // TODO: What to do here?
2516:   }
2517: 
2518:   /**
2519:    * Returns the current time in milliseconds in which clicks gets coalesced
2520:    * into a single <code>ActionEvent</code>.
2521:    *
2522:    * @return the time in milliseconds
2523:    *
2524:    * @since 1.4
2525:    */
2526:   public long getMultiClickThreshhold()
2527:   {
2528:     return multiClickThreshhold;
2529:   }
2530: 
2531:   /**
2532:    * Sets the time in milliseconds in which clicks gets coalesced into a single
2533:    * <code>ActionEvent</code>.
2534:    *
2535:    * @param threshhold the time in milliseconds
2536:    *
2537:    * @since 1.4
2538:    */
2539:   public void setMultiClickThreshhold(long threshhold)
2540:   {
2541:     if (threshhold < 0)
2542:       throw new IllegalArgumentException();
2543: 
2544:     multiClickThreshhold = threshhold;
2545:   }
2546: 
2547:   /**
2548:    * Adds the specified component to this AbstractButton. This overrides the
2549:    * default in order to install an {@link OverlayLayout} layout manager
2550:    * before adding the component. The layout manager is only installed if
2551:    * no other layout manager has been installed before.
2552:    *
2553:    * @param comp the component to be added
2554:    * @param constraints constraints for the layout manager
2555:    * @param index the index at which the component is added
2556:    *
2557:    * @since 1.5
2558:    */
2559:   protected void addImpl(Component comp, Object constraints, int index)
2560:   {
2561:     // We use a client property here, so that no extra memory is used in
2562:     // the common case with no layout manager.
2563:     if (getClientProperty("AbstractButton.customLayoutSet") == null)
2564:       setLayout(new OverlayLayout(this));
2565:     super.addImpl(comp, constraints, index);
2566:   }
2567: 
2568:   /**
2569:    * Sets a layout manager on this AbstractButton. This is overridden in order
2570:    * to detect if the application sets a custom layout manager. If no custom
2571:    * layout manager is set, {@link #addImpl(Component, Object, int)} installs
2572:    * an OverlayLayout before adding a component.
2573:    *
2574:    * @param layout the layout manager to install
2575:    *
2576:    * @since 1.5
2577:    */
2578:   public void setLayout(LayoutManager layout)
2579:   {
2580:     // We use a client property here, so that no extra memory is used in
2581:     // the common case with no layout manager.
2582:     putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE);
2583:     super.setLayout(layout);
2584:   }
2585: 
2586:   /**
2587:    * Helper method for
2588:    * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
2589:    *
2590:    * @param propertyName the name of the property
2591:    * @param value the value of the property
2592:    *
2593:    * @throws IllegalArgumentException if the specified property cannot be set
2594:    *         by this method
2595:    * @throws ClassCastException if the property value does not match the
2596:    *         property type
2597:    * @throws NullPointerException if <code>c</code> or
2598:    *         <code>propertyValue</code> is <code>null</code>
2599:    */
2600:   void setUIProperty(String propertyName, Object value)
2601:   {
2602:     if (propertyName.equals("borderPainted"))
2603:       {
2604:         if (! clientBorderPaintedSet)
2605:           {
2606:             setBorderPainted(((Boolean) value).booleanValue());
2607:             clientBorderPaintedSet = false;
2608:           }
2609:       }
2610:     else if (propertyName.equals("rolloverEnabled"))
2611:       {
2612:         if (! clientRolloverEnabledSet)
2613:           {
2614:             setRolloverEnabled(((Boolean) value).booleanValue());
2615:             clientRolloverEnabledSet = false;
2616:           }
2617:       }
2618:     else if (propertyName.equals("iconTextGap"))
2619:       {
2620:         if (! clientIconTextGapSet)
2621:           {
2622:             setIconTextGap(((Integer) value).intValue());
2623:             clientIconTextGapSet = false;
2624:           }
2625:       }
2626:     else if (propertyName.equals("contentAreaFilled"))
2627:       {
2628:         if (! clientContentAreaFilledSet)
2629:           {
2630:             setContentAreaFilled(((Boolean) value).booleanValue());
2631:             clientContentAreaFilledSet = false;
2632:           }
2633:       }
2634:     else
2635:       {
2636:         super.setUIProperty(propertyName, value);
2637:       }
2638:   }
2639: 
2640:   /**
2641:    * Returns the combined event handler. The instance is created if
2642:    * necessary.
2643:    *
2644:    * @return the combined event handler
2645:    */
2646:   EventHandler getEventHandler()
2647:   {
2648:     if (eventHandler == null)
2649:       eventHandler = new EventHandler();
2650:     return eventHandler;
2651:   }
2652: }