Source for javax.swing.AbstractButton

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