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