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:    * @param a An Action to configure the button from
2037:    */
2038:   protected void configurePropertiesFromAction(Action a)
2039:   {
2040:     if (a == null)
2041:       {
2042:         setText(null);
2043:         setIcon(null);
2044:         setEnabled(true);
2045:         setToolTipText(null);
2046:       }
2047:     else
2048:       {
2049:         setText((String) (a.getValue(Action.NAME)));
2050:         setIcon((Icon) (a.getValue(Action.SMALL_ICON)));
2051:         setEnabled(a.isEnabled());
2052:         setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
2053:         if (a.getValue(Action.MNEMONIC_KEY) != null)
2054:           setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue());
2055:         String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY));
2056: 
2057:         // Set actionCommand to button's text by default if it is not specified
2058:         if (actionCommand != null)
2059:           setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY)));
2060:         else
2061:           setActionCommand(getText());
2062:       }
2063:   }
2064: 
2065:   /**
2066:    * <p>A factory method which should return an {@link ActionListener} that
2067:    * propagates events from the button's {@link ButtonModel} to any of the
2068:    * button's ActionListeners. By default, this is an inner class which
2069:    * calls {@link AbstractButton#fireActionPerformed} with a modified copy
2070:    * of the incoming model {@link ActionEvent}.</p>
2071:    *
2072:    * <p>The button calls this method during construction, stores the
2073:    * resulting ActionListener in its <code>actionListener</code> member
2074:    * field, and subscribes it to the button's model. If the button's model
2075:    * is changed, this listener is unsubscribed from the old model and
2076:    * subscribed to the new one.</p>
2077:    *
2078:    * @return A new ActionListener 
2079:    */
2080:   protected  ActionListener createActionListener()
2081:   {
2082:     return getEventHandler();
2083:   }
2084: 
2085:   /**
2086:    * <p>A factory method which should return a {@link PropertyChangeListener}
2087:    * that accepts changes to the specified {@link Action} and reconfigure
2088:    * the {@link AbstractButton}, by default using the {@link
2089:    * #configurePropertiesFromAction} method.</p>
2090:    *
2091:    * <p>The button calls this method whenever a new Action is assigned to
2092:    * the button's "action" property, via {@link #setAction}, and stores the
2093:    * resulting PropertyChangeListener in its
2094:    * <code>actionPropertyChangeListener</code> member field. The button
2095:    * then subscribes the listener to the button's new action. If the
2096:    * button's action is changed subsequently, the listener is unsubscribed
2097:    * from the old action and subscribed to the new one.</p>
2098:    *
2099:    * @param a The Action which will be listened to, and which should be 
2100:    * the same as the source of any PropertyChangeEvents received by the
2101:    * new listener returned from this method.
2102:    *
2103:    * @return A new PropertyChangeListener
2104:    */
2105:   protected  PropertyChangeListener createActionPropertyChangeListener(Action a)
2106:   {
2107:     return new PropertyChangeListener()
2108:       {
2109:         public void propertyChange(PropertyChangeEvent e)
2110:         {
2111:           Action act = (Action) (e.getSource());
2112:           if (e.getPropertyName().equals("enabled"))
2113:             setEnabled(act.isEnabled());
2114:           else if (e.getPropertyName().equals(Action.NAME))
2115:             setText((String) (act.getValue(Action.NAME)));
2116:           else if (e.getPropertyName().equals(Action.SMALL_ICON))
2117:             setIcon((Icon) (act.getValue(Action.SMALL_ICON)));
2118:           else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION))
2119:             setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION)));
2120:           else if (e.getPropertyName().equals(Action.MNEMONIC_KEY))
2121:             if (act.getValue(Action.MNEMONIC_KEY) != null)
2122:               setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY)))
2123:                           .intValue());
2124:           else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY))
2125:             setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY)));
2126:         }
2127:       };
2128:   }
2129: 
2130:   /**
2131:    * <p>Factory method which creates a {@link ChangeListener}, used to
2132:    * subscribe to ChangeEvents from the button's model. Subclasses of
2133:    * AbstractButton may wish to override the listener used to subscribe to
2134:    * such ChangeEvents. By default, the listener just propagates the
2135:    * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
2136:    * AbstractButton#fireStateChanged} method.</p>
2137:    *
2138:    * <p>The button calls this method during construction, stores the
2139:    * resulting ChangeListener in its <code>changeListener</code> member
2140:    * field, and subscribes it to the button's model. If the button's model
2141:    * is changed, this listener is unsubscribed from the old model and
2142:    * subscribed to the new one.</p>
2143:    *
2144:    * @return The new ChangeListener
2145:    */
2146:   protected ChangeListener createChangeListener()
2147:   {
2148:     return getEventHandler();
2149:   }
2150: 
2151:   /**
2152:    * <p>Factory method which creates a {@link ItemListener}, used to
2153:    * subscribe to ItemEvents from the button's model. Subclasses of
2154:    * AbstractButton may wish to override the listener used to subscribe to
2155:    * such ItemEvents. By default, the listener just propagates the
2156:    * {@link ItemEvent} to the button's ItemListeners, via the {@link
2157:    * AbstractButton#fireItemStateChanged} method.</p>
2158:    *
2159:    * <p>The button calls this method during construction, stores the
2160:    * resulting ItemListener in its <code>changeListener</code> member
2161:    * field, and subscribes it to the button's model. If the button's model
2162:    * is changed, this listener is unsubscribed from the old model and
2163:    * subscribed to the new one.</p>
2164:    *
2165:    * <p>Note that ItemEvents are only generated from the button's model
2166:    * when the model's <em>selected</em> property changes. If you want to
2167:    * subscribe to other properties of the model, you must subscribe to
2168:    * ChangeEvents.
2169:    *
2170:    * @return The new ItemListener
2171:    */
2172:   protected  ItemListener createItemListener()
2173:   {
2174:     return getEventHandler();
2175:   }
2176: 
2177:   /**
2178:    * Programmatically perform a "click" on the button: arming, pressing,
2179:    * waiting, un-pressing, and disarming the model.
2180:    */
2181:   public void doClick()
2182:   {
2183:     doClick(100);
2184:   }
2185: 
2186:   /**
2187:    * Programmatically perform a "click" on the button: arming, pressing,
2188:    * waiting, un-pressing, and disarming the model.
2189:    *
2190:    * @param pressTime The number of milliseconds to wait in the pressed state
2191:    */
2192:   public void doClick(int pressTime)
2193:   {
2194:     ButtonModel mod = getModel();
2195:     if (mod != null)
2196:       {
2197:         mod.setArmed(true);
2198:         mod.setPressed(true);
2199:         try
2200:           {
2201:             java.lang.Thread.sleep(pressTime);
2202:           }
2203:         catch (java.lang.InterruptedException e)
2204:           {
2205:             // probably harmless
2206:           }
2207:         mod.setPressed(false);
2208:         mod.setArmed(false);
2209:       }
2210:   }
2211: 
2212:   /**
2213:    * Return the button's disabled selected icon. The look and feel class
2214:    * should paint this icon when the "enabled" property of the button's model
2215:    * is <code>false</code> and its "selected" property is
2216:    * <code>true</code>. This icon can be <code>null</code>, in which case
2217:    * it is synthesized from the button's selected icon.
2218:    *
2219:    * @return The current disabled selected icon
2220:    */
2221:   public Icon getDisabledSelectedIcon()
2222:   {
2223:     return disabledSelectedIcon;
2224:   }
2225: 
2226:   /**
2227:    * Set the button's disabled selected icon. The look and feel class
2228:    * should paint this icon when the "enabled" property of the button's model
2229:    * is <code>false</code> and its "selected" property is
2230:    * <code>true</code>. This icon can be <code>null</code>, in which case
2231:    * it is synthesized from the button's selected icon.
2232:    *
2233:    * @param icon The new disabled selected icon
2234:    */
2235:   public void setDisabledSelectedIcon(Icon icon)
2236:   {
2237:     if (disabledSelectedIcon == icon)
2238:       return;
2239:     
2240:     Icon old = disabledSelectedIcon;
2241:     disabledSelectedIcon = icon;
2242:     firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
2243:     revalidate();
2244:     repaint();        
2245:   }
2246: 
2247:   /**
2248:    * Return the button's rollover icon. The look and feel class should
2249:    * paint this icon when the "rolloverEnabled" property of the button is
2250:    * <code>true</code> and the mouse rolls over the button.
2251:    *
2252:    * @return The current rollover icon
2253:    */
2254:   public Icon getRolloverIcon()
2255:   {
2256:     return rolloverIcon;
2257:   }
2258: 
2259:   /**
2260:    * Set the button's rollover icon and sets the <code>rolloverEnabled</code>
2261:    * property to <code>true</code>. The look and feel class should
2262:    * paint this icon when the "rolloverEnabled" property of the button is
2263:    * <code>true</code> and the mouse rolls over the button.
2264:    *
2265:    * @param r The new rollover icon
2266:    */
2267:   public void setRolloverIcon(Icon r)
2268:   {
2269:     if (rolloverIcon == r)
2270:       return;
2271:     
2272:     Icon old = rolloverIcon;
2273:     rolloverIcon = r;
2274:     firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
2275:     setRolloverEnabled(true);
2276:     revalidate();
2277:     repaint();
2278:   }
2279: 
2280:   /**
2281:    * Return the button's rollover selected icon. The look and feel class
2282:    * should paint this icon when the "rolloverEnabled" property of the button
2283:    * is <code>true</code>, the "selected" property of the button's model is
2284:    * <code>true</code>, and the mouse rolls over the button.
2285:    *
2286:    * @return The current rollover selected icon
2287:    */
2288:   public Icon getRolloverSelectedIcon()
2289:   {
2290:     return rolloverSelectedIcon;
2291:   }
2292: 
2293:   /**
2294:    * Set the button's rollover selected icon and sets the 
2295:    * <code>rolloverEnabled</code> property to <code>true</code>. The look and 
2296:    * feel class should paint this icon when the "rolloverEnabled" property of 
2297:    * the button is <code>true</code>, the "selected" property of the button's 
2298:    * model is <code>true</code>, and the mouse rolls over the button.
2299:    *
2300:    * @param r The new rollover selected icon.
2301:    */
2302:   public void setRolloverSelectedIcon(Icon r)
2303:   {
2304:     if (rolloverSelectedIcon == r)
2305:       return;
2306:     
2307:     Icon old = rolloverSelectedIcon;
2308:     rolloverSelectedIcon = r;
2309:     firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
2310:     setRolloverEnabled(true);
2311:     revalidate();
2312:     repaint();
2313:   }
2314: 
2315:   /**
2316:    * Return the button's selected icon. The look and feel class should
2317:    * paint this icon when the "selected" property of the button's model is
2318:    * <code>true</code>, and either the "rolloverEnabled" property of the
2319:    * button is <code>false</code> or the mouse is not currently rolled
2320:    * over the button.
2321:    *
2322:    * @return The current selected icon
2323:    */
2324:   public Icon getSelectedIcon()
2325:   {
2326:     return selectedIcon;
2327:   }
2328: 
2329:   /**
2330:    * Set the button's selected icon. The look and feel class should
2331:    * paint this icon when the "selected" property of the button's model is
2332:    * <code>true</code>, and either the "rolloverEnabled" property of the
2333:    * button is <code>false</code> or the mouse is not currently rolled
2334:    * over the button.
2335:    *
2336:    * @param s The new selected icon
2337:    */
2338:   public void setSelectedIcon(Icon s)
2339:   {
2340:     if (selectedIcon == s)
2341:       return;
2342:     
2343:     Icon old = selectedIcon;
2344:     selectedIcon = s;
2345:     firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
2346:     revalidate();
2347:     repaint();
2348:   }
2349: 
2350:   /**
2351:    * Returns an single-element array containing the "text" property of the
2352:    * button if the "selected" property of the button's model is
2353:    * <code>true</code>, otherwise returns <code>null</code>.
2354:    *
2355:    * @return The button's "selected object" array
2356:    */
2357:   public Object[] getSelectedObjects()
2358:   {
2359:     if (isSelected())
2360:       {
2361:         Object[] objs = new Object[1];
2362:         objs[0] = getText();
2363:         return objs;
2364:       }
2365:     else
2366:       {
2367:         return null;
2368:       }
2369:   }
2370: 
2371:   /**
2372:    * Called when image data becomes available for one of the button's icons.
2373:    *
2374:    * @param img The image being updated
2375:    * @param infoflags One of the constant codes in {@link ImageObserver} used
2376:    *     to describe updated portions of an image.
2377:    * @param x X coordinate of the region being updated
2378:    * @param y Y coordinate of the region being updated
2379:    * @param w Width of the region beign updated
2380:    * @param h Height of the region being updated
2381:    *
2382:    * @return <code>true</code> if img is equal to the button's current icon,
2383:    *     otherwise <code>false</code>
2384:    */
2385:   public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
2386:                              int h)
2387:   {
2388:     return current_icon == img;
2389:   }
2390: 
2391:   /**
2392:    * Returns the value of the button's "contentAreaFilled" property. This
2393:    * property indicates whether the area surrounding the text and icon of
2394:    * the button should be filled by the look and feel class.  If this
2395:    * property is <code>false</code>, the look and feel class should leave
2396:    * the content area transparent.
2397:    *
2398:    * @return The current value of the "contentAreaFilled" property
2399:    */
2400:   public boolean isContentAreaFilled()
2401:   {
2402:     return contentAreaFilled;
2403:   }
2404: 
2405:   /**
2406:    * Sets the value of the button's "contentAreaFilled" property. This
2407:    * property indicates whether the area surrounding the text and icon of
2408:    * the button should be filled by the look and feel class.  If this
2409:    * property is <code>false</code>, the look and feel class should leave
2410:    * the content area transparent.
2411:    *
2412:    * @param b The new value of the "contentAreaFilled" property
2413:    */
2414:   public void setContentAreaFilled(boolean b)
2415:   {
2416:     clientContentAreaFilledSet = true;
2417:     if (contentAreaFilled == b)
2418:       return;
2419:     
2420:     // The JDK sets the opaque property to the value of the contentAreaFilled
2421:     // property, so should we do.
2422:     setOpaque(b);
2423:     boolean old = contentAreaFilled;
2424:     contentAreaFilled = b;
2425:     firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
2426:    }
2427: 
2428:   /**
2429:    * Paints the button's border, if the button's "borderPainted" property is
2430:    * <code>true</code>, by out calling to the button's look and feel class.
2431:    *
2432:    * @param g The graphics context used to paint the border
2433:    */
2434:   protected void paintBorder(Graphics g)
2435:   {
2436:     if (isBorderPainted())
2437:       super.paintBorder(g);
2438:   }
2439: 
2440:   /**
2441:    * Returns a string, used only for debugging, which identifies or somehow
2442:    * represents this button. The exact value is implementation-defined.
2443:    *
2444:    * @return A string representation of the button
2445:    */
2446:   protected String paramString()
2447:   {
2448:     StringBuffer sb = new StringBuffer();
2449:     sb.append(super.paramString());
2450:     sb.append(",defaultIcon=");
2451:     if (getIcon() != null)
2452:       sb.append(getIcon());
2453:     sb.append(",disabledIcon=");
2454:     if (getDisabledIcon() != null)
2455:       sb.append(getDisabledIcon());
2456:     sb.append(",disabledSelectedIcon=");
2457:     if (getDisabledSelectedIcon() != null)
2458:       sb.append(getDisabledSelectedIcon());
2459:     sb.append(",margin=");
2460:     if (getMargin() != null)
2461:       sb.append(getMargin());
2462:     sb.append(",paintBorder=").append(isBorderPainted());
2463:     sb.append(",paintFocus=").append(isFocusPainted());
2464:     sb.append(",pressedIcon=");
2465:     if (getPressedIcon() != null)
2466:       sb.append(getPressedIcon());
2467:     sb.append(",rolloverEnabled=").append(isRolloverEnabled());
2468:     sb.append(",rolloverIcon=");
2469:     if (getRolloverIcon() != null)
2470:       sb.append(getRolloverIcon());
2471:     sb.append(",rolloverSelected=");
2472:     if (getRolloverSelectedIcon() != null)
2473:       sb.append(getRolloverSelectedIcon());
2474:     sb.append(",selectedIcon=");
2475:     if (getSelectedIcon() != null)
2476:       sb.append(getSelectedIcon());
2477:     sb.append(",text=");
2478:     if (getText() != null)
2479:       sb.append(getText());
2480:     return sb.toString();
2481:   }
2482: 
2483:   /**
2484:    * Set the "UI" property of the button, which is a look and feel class
2485:    * responsible for handling the button's input events and painting it.
2486:    *
2487:    * @param ui The new "UI" property
2488:    */
2489:   public void setUI(ButtonUI ui)
2490:   {
2491:     super.setUI(ui);
2492:   }
2493:   
2494:   /**
2495:    * Set the "UI" property of the button, which is a look and feel class
2496:    * responsible for handling the button's input events and painting it.
2497:    *
2498:    * @return The current "UI" property
2499:    */
2500:   public ButtonUI getUI()
2501:   {
2502:     return (ButtonUI) ui;
2503:   }
2504:   
2505:   /**
2506:    * Set the "UI" property to a class constructed, via the {@link
2507:    * UIManager}, from the current look and feel. This should be overridden
2508:    * for each subclass of AbstractButton, to retrieve a suitable {@link
2509:    * ButtonUI} look and feel class.
2510:    */
2511:   public void updateUI()
2512:   {
2513:     // TODO: What to do here?
2514:   }
2515: 
2516:   /**
2517:    * Returns the current time in milliseconds in which clicks gets coalesced
2518:    * into a single <code>ActionEvent</code>.
2519:    *
2520:    * @return the time in milliseconds
2521:    * 
2522:    * @since 1.4
2523:    */
2524:   public long getMultiClickThreshhold()
2525:   {
2526:     return multiClickThreshhold;
2527:   }
2528: 
2529:   /**
2530:    * Sets the time in milliseconds in which clicks gets coalesced into a single
2531:    * <code>ActionEvent</code>.
2532:    *
2533:    * @param threshhold the time in milliseconds
2534:    * 
2535:    * @since 1.4
2536:    */
2537:   public void setMultiClickThreshhold(long threshhold)
2538:   {
2539:     if (threshhold < 0)
2540:       throw new IllegalArgumentException();
2541: 
2542:     multiClickThreshhold = threshhold;
2543:   }
2544: 
2545:   /**
2546:    * Adds the specified component to this AbstractButton. This overrides the
2547:    * default in order to install an {@link OverlayLayout} layout manager
2548:    * before adding the component. The layout manager is only installed if
2549:    * no other layout manager has been installed before.
2550:    *
2551:    * @param comp the component to be added
2552:    * @param constraints constraints for the layout manager
2553:    * @param index the index at which the component is added
2554:    *
2555:    * @since 1.5
2556:    */
2557:   protected void addImpl(Component comp, Object constraints, int index)
2558:   {
2559:     // We use a client property here, so that no extra memory is used in
2560:     // the common case with no layout manager.
2561:     if (getClientProperty("AbstractButton.customLayoutSet") == null)
2562:       setLayout(new OverlayLayout(this));
2563:     super.addImpl(comp, constraints, index);
2564:   }
2565: 
2566:   /**
2567:    * Sets a layout manager on this AbstractButton. This is overridden in order
2568:    * to detect if the application sets a custom layout manager. If no custom
2569:    * layout manager is set, {@link #addImpl(Component, Object, int)} installs
2570:    * an OverlayLayout before adding a component.
2571:    *
2572:    * @param layout the layout manager to install
2573:    *
2574:    * @since 1.5
2575:    */
2576:   public void setLayout(LayoutManager layout)
2577:   {
2578:     // We use a client property here, so that no extra memory is used in
2579:     // the common case with no layout manager.
2580:     putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE);
2581:     super.setLayout(layout);
2582:   }
2583: 
2584:   /**
2585:    * Helper method for
2586:    * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
2587:    * 
2588:    * @param propertyName the name of the property
2589:    * @param value the value of the property
2590:    *
2591:    * @throws IllegalArgumentException if the specified property cannot be set
2592:    *         by this method
2593:    * @throws ClassCastException if the property value does not match the
2594:    *         property type
2595:    * @throws NullPointerException if <code>c</code> or
2596:    *         <code>propertyValue</code> is <code>null</code>
2597:    */
2598:   void setUIProperty(String propertyName, Object value)
2599:   {
2600:     if (propertyName.equals("borderPainted"))
2601:       {
2602:         if (! clientBorderPaintedSet)
2603:           {
2604:             setBorderPainted(((Boolean) value).booleanValue());
2605:             clientBorderPaintedSet = false;
2606:           }
2607:       }
2608:     else if (propertyName.equals("rolloverEnabled"))
2609:       {
2610:         if (! clientRolloverEnabledSet)
2611:           {
2612:             setRolloverEnabled(((Boolean) value).booleanValue());
2613:             clientRolloverEnabledSet = false;
2614:           }
2615:       }
2616:     else if (propertyName.equals("iconTextGap"))
2617:       {
2618:         if (! clientIconTextGapSet)
2619:           {
2620:             setIconTextGap(((Integer) value).intValue());
2621:             clientIconTextGapSet = false;
2622:           }
2623:       }
2624:     else if (propertyName.equals("contentAreaFilled"))
2625:       {
2626:         if (! clientContentAreaFilledSet)
2627:           {
2628:             setContentAreaFilled(((Boolean) value).booleanValue());
2629:             clientContentAreaFilledSet = false;
2630:           }
2631:       }
2632:     else
2633:       {
2634:         super.setUIProperty(propertyName, value);
2635:       }
2636:   }
2637: 
2638:   /**
2639:    * Returns the combined event handler. The instance is created if
2640:    * necessary.
2641:    *
2642:    * @return the combined event handler
2643:    */
2644:   EventHandler getEventHandler()
2645:   {
2646:     if (eventHandler == null)
2647:       eventHandler = new EventHandler();
2648:     return eventHandler;
2649:   }
2650: }