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