Source for javax.swing.AbstractButton

   1: /* AbstractButton.java -- Provides basic button functionality.
   2:    Copyright (C) 2002, 2004 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 disabeldIcon;
 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 miliseconds 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 curren 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 maxinimum accessible value for the AccessibleAbstractButton,
 655:      *         which is <code>1</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:         this.text = 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>RIGHT</code>.
1301:    * 
1302:    * @return The current horizontal alignment
1303:    */
1304:   public int getHorizontalAlignment()
1305:   {
1306:     return horizontalAlignment;
1307:   }
1308: 
1309:   /**
1310:    * Set the horizontal alignment of the button's text and icon. The
1311:    * alignment is a numeric constant from {@link SwingConstants}. It must
1312:    * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1313:    * <code>LEADING</code> or <code>TRAILING</code>.  The default is
1314:    * <code>RIGHT</code>.
1315:    *
1316:    * @param a The new horizontal alignment
1317:    * @throws IllegalArgumentException If alignment is not one of the legal
1318:    * constants.
1319:    */
1320:   public void setHorizontalAlignment(int a)
1321:   {
1322:     if (horizontalAlignment == a)
1323:       return;
1324: 
1325:     int old = horizontalAlignment;
1326:     horizontalAlignment = a;
1327:     firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1328:     revalidate();
1329:     repaint();
1330:   }
1331: 
1332:   /**
1333:    * Get the horizontal position of the button's text relative to its
1334:    * icon. The position is a numeric constant from {@link
1335:    * SwingConstants}. It must be one of: <code>RIGHT</code>,
1336:    * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1337:    * <code>TRAILING</code>.  The default is <code>TRAILING</code>.
1338:    *
1339:    * @return The current horizontal text position
1340:    */
1341:   public int getHorizontalTextPosition()
1342:   {
1343:     return horizontalTextPosition;
1344:   }
1345: 
1346:   /**
1347:    * Set the horizontal position of the button's text relative to its
1348:    * icon. The position is a numeric constant from {@link
1349:    * SwingConstants}. It must be one of: <code>RIGHT</code>,
1350:    * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1351:    * <code>TRAILING</code>. The default is <code>TRAILING</code>.
1352:    *
1353:    * @param t The new horizontal text position
1354:    * @throws IllegalArgumentException If position is not one of the legal
1355:    * constants.
1356:    */
1357:   public void setHorizontalTextPosition(int t)
1358:   {
1359:     if (horizontalTextPosition == t)
1360:       return;
1361: 
1362:     int old = horizontalTextPosition;
1363:     horizontalTextPosition = t;
1364:     firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1365:     revalidate();
1366:     repaint();
1367:   }
1368: 
1369:   /**
1370:    * Get the vertical alignment of the button's text and icon. The
1371:    * alignment is a numeric constant from {@link SwingConstants}. It must
1372:    * be one of: <code>CENTER</code>, <code>TOP</code>, or
1373:    * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1374:    *
1375:    * @return The current vertical alignment
1376:    */
1377:   public int getVerticalAlignment()
1378:   {
1379:     return verticalAlignment;
1380:   }
1381: 
1382:   /**
1383:    * Set the vertical alignment of the button's text and icon. The
1384:    * alignment is a numeric constant from {@link SwingConstants}. It must
1385:    * be one of: <code>CENTER</code>, <code>TOP</code>, or
1386:    * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1387:    *
1388:    * @param a The new vertical alignment
1389:    * @throws IllegalArgumentException If alignment is not one of the legal
1390:    * constants.
1391:    */
1392:   public void setVerticalAlignment(int a)
1393:   {
1394:     if (verticalAlignment == a)
1395:       return;
1396:     
1397:     int old = verticalAlignment;
1398:     verticalAlignment = a;
1399:     firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1400:     revalidate();
1401:     repaint();
1402:   }
1403: 
1404:   /**
1405:    * Get the vertical position of the button's text relative to its
1406:    * icon. The alignment is a numeric constant from {@link
1407:    * SwingConstants}. It must be one of: <code>CENTER</code>,
1408:    * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1409:    * <code>CENTER</code>.
1410:    *
1411:    * @return The current vertical position
1412:    */
1413:   public int getVerticalTextPosition()
1414:   {
1415:     return verticalTextPosition;
1416:   }
1417: 
1418:   /**
1419:    * Set the vertical position of the button's text relative to its
1420:    * icon. The alignment is a numeric constant from {@link
1421:    * SwingConstants}. It must be one of: <code>CENTER</code>,
1422:    * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1423:    * <code>CENTER</code>.
1424:    *
1425:    * @param t The new vertical position
1426:    * @throws IllegalArgumentException If position is not one of the legal
1427:    * constants.
1428:    */
1429:   public void setVerticalTextPosition(int t)
1430:   {
1431:     if (verticalTextPosition == t)
1432:       return;
1433:     
1434:     int old = verticalTextPosition;
1435:     verticalTextPosition = t;
1436:     firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1437:     revalidate();
1438:     repaint();
1439:   }
1440: 
1441:   /**
1442:    * Set the value of the "borderPainted" property. If set to
1443:    * <code>false</code>, the button's look and feel class should not paint
1444:    * a border for the button. The default is <code>true</code>.
1445:    *
1446:    * @return The current value of the property.
1447:    */
1448:   public boolean isBorderPainted()
1449:   {
1450:     return borderPainted;
1451:   }
1452: 
1453:   /**
1454:    * Set the value of the "borderPainted" property. If set to
1455:    * <code>false</code>, the button's look and feel class should not paint
1456:    * a border for the button. The default is <code>true</code>.
1457:    *
1458:    * @param b The new value of the property.
1459:    */
1460:   public void setBorderPainted(boolean b)
1461:   {
1462:     clientBorderPaintedSet = true;
1463:     if (borderPainted == b)
1464:       return;
1465:     boolean old = borderPainted;
1466:     borderPainted = b;
1467:     firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
1468:     revalidate();
1469:     repaint();
1470:   }
1471: 
1472:   /**
1473:    * Get the value of the "action" property. 
1474:    *
1475:    * @return The current value of the "action" property
1476:    */
1477:   public Action getAction()
1478:   {
1479:     return action;
1480:   }
1481: 
1482:   /**
1483:    * <p>Set the button's "action" property, subscribing the new action to the
1484:    * button, as an ActionListener, if it is not already subscribed. The old
1485:    * Action, if it exists, is unsubscribed, and the button is unsubscribed
1486:    * from the old Action if it was previously subscribed as a
1487:    * PropertyChangeListener.</p>
1488:    *
1489:    * <p>This method also configures several of the button's properties from
1490:    * the Action, by calling {@link #configurePropertiesFromAction}, and
1491:    * subscribes the button to the Action as a PropertyChangeListener.
1492:    * Subsequent changes to the Action will thus reconfigure the button 
1493:    * automatically.</p>
1494:    *
1495:    * @param a The new value of the "action" property
1496:    */
1497:   public void setAction(Action a)
1498:   {
1499:     if (action != null)
1500:       {
1501:         action.removePropertyChangeListener(actionPropertyChangeListener);
1502:         removeActionListener(action);
1503:         if (actionPropertyChangeListener != null)
1504:           {
1505:             action.removePropertyChangeListener(actionPropertyChangeListener);
1506:             actionPropertyChangeListener = null;
1507:           }
1508:       }
1509: 
1510:     Action old = action;
1511:     action = a;
1512:     configurePropertiesFromAction(action);
1513:     if (action != null)
1514:       {
1515:         actionPropertyChangeListener = createActionPropertyChangeListener(a);      
1516:         action.addPropertyChangeListener(actionPropertyChangeListener);
1517:         addActionListener(action);
1518:       }
1519:   }
1520: 
1521:   /**
1522:    * Return the button's default "icon" property.
1523:    *
1524:    * @return The current default icon
1525:    */
1526:   public Icon getIcon()
1527:   {
1528:     return default_icon;
1529:   }
1530: 
1531:   /**
1532:    * Set the button's default "icon" property. This icon is used as a basis
1533:    * for the pressed and disabled icons, if none are explicitly set.
1534:    *
1535:    * @param i The new default icon
1536:    */
1537:   public void setIcon(Icon i)
1538:   {
1539:     if (default_icon == i)
1540:       return;
1541:     
1542:     Icon old = default_icon;      
1543:     default_icon = i;      
1544:     firePropertyChange(ICON_CHANGED_PROPERTY, old, i);
1545:     revalidate();
1546:     repaint();
1547:   }
1548: 
1549:   /**
1550:    * Return the button's "text" property. This property is synonymous with
1551:    * the "label" property.
1552:    *
1553:    * @return The current "text" property
1554:    */
1555:   public String getText()
1556:   {
1557:     return text;
1558:   }
1559: 
1560:   /**
1561:    * Set the button's "label" property. This property is synonymous with the
1562:    * "text" property.
1563:    *
1564:    * @param label The new "label" property
1565:    *
1566:    * @deprecated use <code>setText(text)</code>
1567:    */
1568:   public void setLabel(String label)
1569:   {
1570:     setText(label);
1571:   }
1572: 
1573:   /**
1574:    * Return the button's "label" property. This property is synonymous with
1575:    * the "text" property.
1576:    *
1577:    * @return The current "label" property
1578:    *
1579:    * @deprecated use <code>getText()</code>
1580:    */
1581:   public String getLabel()
1582:   {
1583:     return getText();
1584:   }
1585: 
1586:   /**
1587:    * Set the button's "text" property. This property is synonymous with the
1588:    * "label" property.
1589:    *
1590:    * @param t The new "text" property
1591:    */
1592:   public void setText(String t)
1593:   {
1594:     if (text == t)
1595:       return;
1596:     
1597:     String old = text;
1598:     text = t;
1599:     firePropertyChange(TEXT_CHANGED_PROPERTY, old, t);
1600:     revalidate();
1601:     repaint();
1602:   }
1603: 
1604:   /**
1605:    * Set the value of the {@link #iconTextGap} property.
1606:    * 
1607:    * @param i The new value of the property
1608:    * 
1609:    * @since 1.4
1610:    */
1611:   public void setIconTextGap(int i)
1612:   {
1613:     clientIconTextGapSet = true;
1614:     if (iconTextGap == i)
1615:       return;
1616:     
1617:     int old = iconTextGap;
1618:     iconTextGap = i;
1619:     firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
1620:     revalidate();
1621:     repaint();
1622:   }
1623: 
1624:   /**
1625:    * Get the value of the {@link #iconTextGap} property.
1626:    *
1627:    * @return The current value of the property
1628:    * 
1629:    * @since 1.4
1630:    */
1631:   public int getIconTextGap()
1632:   {
1633:     return iconTextGap;
1634:   }
1635: 
1636:   /**
1637:    * Return the button's "margin" property, which is an {@link Insets} object
1638:    * describing the distance between the button's border and its text and
1639:    * icon.
1640:    *
1641:    * @return The current "margin" property
1642:    */
1643:   public Insets getMargin()
1644:   {
1645:     return margin;
1646:   }
1647: 
1648:   /**
1649:    * Set the button's "margin" property, which is an {@link Insets} object
1650:    * describing the distance between the button's border and its text and
1651:    * icon.
1652:    *
1653:    * @param m The new "margin" property
1654:    */
1655:   public void setMargin(Insets m)
1656:   {
1657:     if (margin == m)
1658:       return;
1659:     
1660:     Insets old = margin;
1661:     margin = m;
1662:     firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
1663:     revalidate();
1664:     repaint();
1665:   }
1666: 
1667:   /**
1668:    * Return the button's "pressedIcon" property. The look and feel class
1669:    * should paint this icon when the "pressed" property of the button's
1670:    * {@link ButtonModel} is <code>true</code>. This property may be
1671:    * <code>null</code>, in which case the default icon is used.
1672:    *
1673:    * @return The current "pressedIcon" property
1674:    */
1675:   public Icon getPressedIcon()
1676:   {
1677:     return pressed_icon;
1678:   }
1679: 
1680:   /**
1681:    * Set the button's "pressedIcon" property. The look and feel class
1682:    * should paint this icon when the "pressed" property of the button's
1683:    * {@link ButtonModel} is <code>true</code>. This property may be
1684:    * <code>null</code>, in which case the default icon is used.
1685:    *
1686:    * @param pressedIcon The new "pressedIcon" property
1687:    */
1688:   public void setPressedIcon(Icon pressedIcon)
1689:   {
1690:     if (pressed_icon == pressedIcon)
1691:       return;
1692:     
1693:     Icon old = pressed_icon;
1694:     pressed_icon = pressedIcon;
1695:     firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon);
1696:     revalidate();
1697:     repaint();
1698:   }
1699: 
1700:   /**
1701:    * Return the button's "disabledIcon" property. The look and feel class
1702:    * should paint this icon when the "enabled" property of the button's
1703:    * {@link ButtonModel} is <code>false</code>. This property may be
1704:    * <code>null</code>, in which case an icon is constructed, based on the
1705:    * default icon.
1706:    *
1707:    * @return The current "disabledIcon" property
1708:    */
1709:   public Icon getDisabledIcon()
1710:   {
1711:     if (disabeldIcon == null && default_icon instanceof ImageIcon)
1712:       {
1713:         Image iconImage = ((ImageIcon) default_icon).getImage();
1714:         Image grayImage = GrayFilter.createDisabledImage(iconImage);
1715:         disabeldIcon = new ImageIcon(grayImage);
1716:       }
1717:       
1718:     return disabeldIcon;
1719:   }
1720: 
1721:   /**
1722:    * Set the button's "disabledIcon" property. The look and feel class should
1723:    * paint this icon when the "enabled" property of the button's {@link
1724:    * ButtonModel} is <code>false</code>. This property may be
1725:    * <code>null</code>, in which case an icon is constructed, based on the
1726:    * default icon.
1727:    *
1728:    * @param d The new "disabledIcon" property
1729:    */
1730:   public void setDisabledIcon(Icon d)
1731:   {
1732:     disabeldIcon = d;
1733:     revalidate();
1734:     repaint();
1735:   }
1736: 
1737:   /**
1738:    * Return the button's "paintFocus" property. This property controls
1739:    * whether or not the look and feel class will paint a special indicator
1740:    * of focus state for the button. If it is false, the button still paints
1741:    * when focused, but no special decoration is painted to indicate the
1742:    * presence of focus.
1743:    *
1744:    * @return The current "paintFocus" property
1745:    */
1746:   public boolean isFocusPainted()
1747:   {
1748:     return focusPainted;
1749:   }
1750: 
1751:   /**
1752:    * Set the button's "paintFocus" property. This property controls whether
1753:    * or not the look and feel class will paint a special indicator of focus
1754:    * state for the button. If it is false, the button still paints when
1755:    * focused, but no special decoration is painted to indicate the presence
1756:    * of focus.
1757:    *
1758:    * @param p The new "paintFocus" property
1759:    */
1760:   public void setFocusPainted(boolean p)
1761:   {
1762:     if (focusPainted == p)
1763:       return;
1764:     
1765:     boolean old = focusPainted;
1766:     focusPainted = p;
1767:     firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p);
1768:     revalidate();
1769:     repaint();
1770:   }
1771: 
1772:   /**
1773:    * Verifies that a particular key is one of the valid constants used for
1774:    * describing horizontal alignment and positioning. The valid constants
1775:    * are the following members of {@link SwingConstants}:
1776:    * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1777:    * <code>LEADING</code> or <code>TRAILING</code>.
1778:    *
1779:    * @param key The key to check
1780:    * @param exception A message to include in an IllegalArgumentException
1781:    *
1782:    * @return the value of key
1783:    *
1784:    * @throws IllegalArgumentException If key is not one of the valid constants
1785:    *
1786:    * @see #setHorizontalTextPosition(int)
1787:    * @see #setHorizontalAlignment(int)
1788:    */
1789:   protected  int checkHorizontalKey(int key, String exception)
1790:   {
1791:     switch (key)
1792:       {
1793:       case SwingConstants.RIGHT:
1794:       case SwingConstants.LEFT:
1795:       case SwingConstants.CENTER:
1796:       case SwingConstants.LEADING:
1797:       case SwingConstants.TRAILING:
1798:         break;
1799:       default:
1800:         throw new IllegalArgumentException(exception);
1801:       }
1802:     return key;
1803:   }
1804: 
1805:   /**
1806:    * Verifies that a particular key is one of the valid constants used for
1807:    * describing vertical alignment and positioning. The valid constants are
1808:    * the following members of {@link SwingConstants}: <code>TOP</code>,
1809:    * <code>BOTTOM</code> or <code>CENTER</code>.
1810:    *
1811:    * @param key The key to check
1812:    * @param exception A message to include in an IllegalArgumentException
1813:    *
1814:    * @return the value of key
1815:    *
1816:    * @throws IllegalArgumentException If key is not one of the valid constants
1817:    *
1818:    * @see #setVerticalTextPosition(int)
1819:    * @see #setVerticalAlignment(int)
1820:    */
1821:   protected  int checkVerticalKey(int key, String exception)
1822:   {
1823:     switch (key)
1824:       {
1825:       case SwingConstants.TOP:
1826:       case SwingConstants.BOTTOM:
1827:       case SwingConstants.CENTER:
1828:         break;
1829:       default:
1830:         throw new IllegalArgumentException(exception);
1831:       }
1832:     return key;
1833:   }
1834: 
1835:   /**
1836:    * Configure various properties of the button by reading properties
1837:    * of an {@link Action}. The mapping of properties is as follows:
1838:    *
1839:    * <table>
1840:    *
1841:    * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr>
1842:    *
1843:    * <tr><td>NAME                 </td> <td>text                   </td></tr>
1844:    * <tr><td>SMALL_ICON           </td> <td>icon                   </td></tr>
1845:    * <tr><td>SHORT_DESCRIPTION    </td> <td>toolTipText            </td></tr>
1846:    * <tr><td>MNEMONIC_KEY         </td> <td>mnemonic               </td></tr>
1847:    * <tr><td>ACTION_COMMAND_KEY   </td> <td>actionCommand          </td></tr>
1848:    *
1849:    * </table>
1850:    *
1851:    * <p>In addition, this method always sets the button's "enabled" property to
1852:    * the value of the Action's "enabled" property.</p>
1853:    *
1854:    * <p>If the provided Action is <code>null</code>, the text, icon, and
1855:    * toolTipText properties of the button are set to <code>null</code>, and
1856:    * the "enabled" property is set to <code>true</code>; the mnemonic and
1857:    * actionCommand properties are unchanged.</p>
1858:    *
1859:    * @param a An Action to configure the button from
1860:    */
1861:   protected void configurePropertiesFromAction(Action a)
1862:   {
1863:     if (a == null)
1864:       {
1865:         setText(null);
1866:         setIcon(null);
1867:         setEnabled(true);
1868:         setToolTipText(null);
1869:       }
1870:     else
1871:       {
1872:         setText((String) (a.getValue(Action.NAME)));
1873:         setIcon((Icon) (a.getValue(Action.SMALL_ICON)));
1874:         setEnabled(a.isEnabled());
1875:         setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
1876:         if (a.getValue(Action.MNEMONIC_KEY) != null)
1877:           setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue());
1878:         String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY));
1879: 
1880:         // Set actionCommand to button's text by default if it is not specified
1881:         if (actionCommand != null)
1882:           setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY)));
1883:         else
1884:           setActionCommand(getText());
1885:       }
1886:   }
1887: 
1888:   /**
1889:    * <p>A factory method which should return an {@link ActionListener} that
1890:    * propagates events from the button's {@link ButtonModel} to any of the
1891:    * button's ActionListeners. By default, this is an inner class which
1892:    * calls {@link AbstractButton#fireActionPerformed} with a modified copy
1893:    * of the incoming model {@link ActionEvent}.</p>
1894:    *
1895:    * <p>The button calls this method during construction, stores the
1896:    * resulting ActionListener in its <code>actionListener</code> member
1897:    * field, and subscribes it to the button's model. If the button's model
1898:    * is changed, this listener is unsubscribed from the old model and
1899:    * subscribed to the new one.</p>
1900:    *
1901:    * @return A new ActionListener 
1902:    */
1903:   protected  ActionListener createActionListener()
1904:   {
1905:     return new ActionListener()
1906:       {
1907:         public void actionPerformed(ActionEvent e)
1908:         {
1909:           AbstractButton.this.fireActionPerformed(e);
1910:         }
1911:       };
1912:   }
1913: 
1914:   /**
1915:    * <p>A factory method which should return a {@link PropertyChangeListener}
1916:    * that accepts changes to the specified {@link Action} and reconfigure
1917:    * the {@link AbstractButton}, by default using the {@link
1918:    * #configurePropertiesFromAction} method.</p>
1919:    *
1920:    * <p>The button calls this method whenever a new Action is assigned to
1921:    * the button's "action" property, via {@link #setAction}, and stores the
1922:    * resulting PropertyChangeListener in its
1923:    * <code>actionPropertyChangeListener</code> member field. The button
1924:    * then subscribes the listener to the button's new action. If the
1925:    * button's action is changed subsequently, the listener is unsubscribed
1926:    * from the old action and subscribed to the new one.</p>
1927:    *
1928:    * @param a The Action which will be listened to, and which should be 
1929:    * the same as the source of any PropertyChangeEvents received by the
1930:    * new listener returned from this method.
1931:    *
1932:    * @return A new PropertyChangeListener
1933:    */
1934:   protected  PropertyChangeListener createActionPropertyChangeListener(Action a)
1935:   {
1936:     return new PropertyChangeListener()
1937:       {
1938:         public void propertyChange(PropertyChangeEvent e)
1939:         {
1940:           Action act = (Action) (e.getSource());
1941:           if (e.getPropertyName().equals("enabled"))
1942:             setEnabled(act.isEnabled());
1943:           else if (e.getPropertyName().equals(Action.NAME))
1944:             setText((String) (act.getValue(Action.NAME)));
1945:           else if (e.getPropertyName().equals(Action.SMALL_ICON))
1946:             setIcon((Icon) (act.getValue(Action.SMALL_ICON)));
1947:           else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION))
1948:             setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION)));
1949:           else if (e.getPropertyName().equals(Action.MNEMONIC_KEY))
1950:             if (act.getValue(Action.MNEMONIC_KEY) != null)
1951:               setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY)))
1952:                           .intValue());
1953:           else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY))
1954:             setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY)));
1955:         }
1956:       };
1957:   }
1958: 
1959:   /**
1960:    * <p>Factory method which creates a {@link ChangeListener}, used to
1961:    * subscribe to ChangeEvents from the button's model. Subclasses of
1962:    * AbstractButton may wish to override the listener used to subscribe to
1963:    * such ChangeEvents. By default, the listener just propagates the
1964:    * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
1965:    * AbstractButton#fireStateChanged} method.</p>
1966:    *
1967:    * <p>The button calls this method during construction, stores the
1968:    * resulting ChangeListener in its <code>changeListener</code> member
1969:    * field, and subscribes it to the button's model. If the button's model
1970:    * is changed, this listener is unsubscribed from the old model and
1971:    * subscribed to the new one.</p>
1972:    *
1973:    * @return The new ChangeListener
1974:    */
1975:   protected ChangeListener createChangeListener()
1976:   {
1977:     return new ButtonChangeListener();
1978:   }
1979: 
1980:   /**
1981:    * <p>Factory method which creates a {@link ItemListener}, used to
1982:    * subscribe to ItemEvents from the button's model. Subclasses of
1983:    * AbstractButton may wish to override the listener used to subscribe to
1984:    * such ItemEvents. By default, the listener just propagates the
1985:    * {@link ItemEvent} to the button's ItemListeners, via the {@link
1986:    * AbstractButton#fireItemStateChanged} method.</p>
1987:    *
1988:    * <p>The button calls this method during construction, stores the
1989:    * resulting ItemListener 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:    * <p>Note that ItemEvents are only generated from the button's model
1995:    * when the model's <em>selected</em> property changes. If you want to
1996:    * subscribe to other properties of the model, you must subscribe to
1997:    * ChangeEvents.
1998:    *
1999:    * @return The new ItemListener
2000:    */
2001:   protected  ItemListener createItemListener()
2002:   {
2003:     return new ItemListener()
2004:       {
2005:         public void itemStateChanged(ItemEvent e)
2006:         {
2007:           AbstractButton.this.fireItemStateChanged(e);
2008:         }
2009:       };
2010:   }
2011: 
2012:   /**
2013:    * Programmatically perform a "click" on the button: arming, pressing,
2014:    * waiting, un-pressing, and disarming the model.
2015:    */
2016:   public void doClick()
2017:   {
2018:     doClick(100);
2019:   }
2020: 
2021:   /**
2022:    * Programmatically perform a "click" on the button: arming, pressing,
2023:    * waiting, un-pressing, and disarming the model.
2024:    *
2025:    * @param pressTime The number of milliseconds to wait in the pressed state
2026:    */
2027:   public void doClick(int pressTime)
2028:   {
2029:     ButtonModel mod = getModel();
2030:     if (mod != null)
2031:       {
2032:         mod.setArmed(true);
2033:         mod.setPressed(true);
2034:         try
2035:           {
2036:             java.lang.Thread.sleep(pressTime);
2037:           }
2038:         catch (java.lang.InterruptedException e)
2039:           {
2040:             // probably harmless
2041:           }
2042:         mod.setPressed(false);
2043:         mod.setArmed(false);
2044:       }
2045:   }
2046: 
2047:   /**
2048:    * Return the button's disabled selected icon. The look and feel class
2049:    * should paint this icon when the "enabled" property of the button's model
2050:    * is <code>false</code> and its "selected" property is
2051:    * <code>true</code>. This icon can be <code>null</code>, in which case
2052:    * it is synthesized from the button's selected icon.
2053:    *
2054:    * @return The current disabled selected icon
2055:    */
2056:   public Icon getDisabledSelectedIcon()
2057:   {
2058:     return disabledSelectedIcon;
2059:   }
2060: 
2061:   /**
2062:    * Set the button's disabled selected icon. The look and feel class
2063:    * should paint this icon when the "enabled" property of the button's model
2064:    * is <code>false</code> and its "selected" property is
2065:    * <code>true</code>. This icon can be <code>null</code>, in which case
2066:    * it is synthesized from the button's selected icon.
2067:    *
2068:    * @param icon The new disabled selected icon
2069:    */
2070:   public void setDisabledSelectedIcon(Icon icon)
2071:   {
2072:     if (disabledSelectedIcon == icon)
2073:       return;
2074:     
2075:     Icon old = disabledSelectedIcon;
2076:     disabledSelectedIcon = icon;
2077:     firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
2078:     revalidate();
2079:     repaint();        
2080:   }
2081: 
2082:   /**
2083:    * Return the button's rollover icon. The look and feel class should
2084:    * paint this icon when the "rolloverEnabled" property of the button is
2085:    * <code>true</code> and the mouse rolls over the button.
2086:    *
2087:    * @return The current rollover icon
2088:    */
2089:   public Icon getRolloverIcon()
2090:   {
2091:     return rolloverIcon;
2092:   }
2093: 
2094:   /**
2095:    * Set the button's rollover icon. The look and feel class should
2096:    * paint this icon when the "rolloverEnabled" property of the button is
2097:    * <code>true</code> and the mouse rolls over the button.
2098:    *
2099:    * @param r The new rollover icon
2100:    */
2101:   public void setRolloverIcon(Icon r)
2102:   {
2103:     if (rolloverIcon == r)
2104:       return;
2105:     
2106:     Icon old = rolloverIcon;
2107:     rolloverIcon = r;
2108:     firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
2109:     revalidate();
2110:     repaint();
2111:   }
2112: 
2113:   /**
2114:    * Return the button's rollover selected icon. The look and feel class
2115:    * should paint this icon when the "rolloverEnabled" property of the button
2116:    * is <code>true</code>, the "selected" property of the button's model is
2117:    * <code>true</code>, and the mouse rolls over the button.
2118:    *
2119:    * @return The current rollover selected icon
2120:    */
2121:   public Icon getRolloverSelectedIcon()
2122:   {
2123:     return rolloverSelectedIcon;
2124:   }
2125: 
2126:   /**
2127:    * Set the button's rollover selected icon. The look and feel class
2128:    * should paint this icon when the "rolloverEnabled" property of the button
2129:    * is <code>true</code>, the "selected" property of the button's model is
2130:    * <code>true</code>, and the mouse rolls over the button.
2131:    *
2132:    * @param r The new rollover selected icon
2133:    */
2134:   public void setRolloverSelectedIcon(Icon r)
2135:   {
2136:     if (rolloverSelectedIcon == r)
2137:       return;
2138:     
2139:     Icon old = rolloverSelectedIcon;
2140:     rolloverSelectedIcon = r;
2141:     firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
2142:     revalidate();
2143:     repaint();
2144:   }
2145: 
2146:   /**
2147:    * Return the button's selected icon. The look and feel class should
2148:    * paint this icon when the "selected" property of the button's model is
2149:    * <code>true</code>, and either the "rolloverEnabled" property of the
2150:    * button is <code>false</code> or the mouse is not currently rolled
2151:    * over the button.
2152:    *
2153:    * @return The current selected icon
2154:    */
2155:   public Icon getSelectedIcon()
2156:   {
2157:     return selectedIcon;
2158:   }
2159: 
2160:   /**
2161:    * Set the button's selected icon. The look and feel class should
2162:    * paint this icon when the "selected" property of the button's model is
2163:    * <code>true</code>, and either the "rolloverEnabled" property of the
2164:    * button is <code>false</code> or the mouse is not currently rolled
2165:    * over the button.
2166:    *
2167:    * @param s The new selected icon
2168:    */
2169:   public void setSelectedIcon(Icon s)
2170:   {
2171:     if (selectedIcon == s)
2172:       return;
2173:     
2174:     Icon old = selectedIcon;
2175:     selectedIcon = s;
2176:     firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
2177:     revalidate();
2178:     repaint();
2179:   }
2180: 
2181:   /**
2182:    * Returns an single-element array containing the "text" property of the
2183:    * button if the "selected" property of the button's model is
2184:    * <code>true</code>, otherwise returns <code>null</code>.
2185:    *
2186:    * @return The button's "selected object" array
2187:    */
2188:   public Object[] getSelectedObjects()
2189:   {
2190:     if (isSelected())
2191:       {
2192:         Object[] objs = new Object[1];
2193:         objs[0] = getText();
2194:         return objs;
2195:       }
2196:     else
2197:       {
2198:         return null;
2199:       }
2200:   }
2201: 
2202:   /**
2203:    * Called when image data becomes available for one of the button's icons.
2204:    *
2205:    * @param img The image being updated
2206:    * @param infoflags One of the constant codes in {@link ImageObserver} used
2207:    *     to describe updated portions of an image.
2208:    * @param x X coordinate of the region being updated
2209:    * @param y Y coordinate of the region being updated
2210:    * @param w Width of the region beign updated
2211:    * @param h Height of the region being updated
2212:    *
2213:    * @return <code>true</code> if img is equal to the button's current icon,
2214:    *     otherwise <code>false</code>
2215:    */
2216:   public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
2217:                              int h)
2218:   {
2219:     return current_icon == img;
2220:   }
2221: 
2222:   /**
2223:    * Returns the value of the button's "contentAreaFilled" property. This
2224:    * property indicates whether the area surrounding the text and icon of
2225:    * the button should be filled by the look and feel class.  If this
2226:    * property is <code>false</code>, the look and feel class should leave
2227:    * the content area transparent.
2228:    *
2229:    * @return The current value of the "contentAreaFilled" property
2230:    */
2231:   public boolean isContentAreaFilled()
2232:   {
2233:     return contentAreaFilled;
2234:   }
2235: 
2236:   /**
2237:    * Sets the value of the button's "contentAreaFilled" property. This
2238:    * property indicates whether the area surrounding the text and icon of
2239:    * the button should be filled by the look and feel class.  If this
2240:    * property is <code>false</code>, the look and feel class should leave
2241:    * the content area transparent.
2242:    *
2243:    * @param b The new value of the "contentAreaFilled" property
2244:    */
2245:   public void setContentAreaFilled(boolean b)
2246:   {
2247:     clientContentAreaFilledSet = true;
2248:     if (contentAreaFilled == b)
2249:       return;
2250:     
2251:     // The JDK sets the opaque property to the value of the contentAreaFilled
2252:     // property, so should we do.
2253:     setOpaque(b);
2254:     boolean old = contentAreaFilled;
2255:     contentAreaFilled = b;
2256:     firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
2257:    }
2258: 
2259:   /**
2260:    * Paints the button's border, if the button's "borderPainted" property is
2261:    * <code>true</code>, by out calling to the button's look and feel class.
2262:    *
2263:    * @param g The graphics context used to paint the border
2264:    */
2265:   protected void paintBorder(Graphics g)
2266:   {
2267:     if (isBorderPainted())
2268:       super.paintBorder(g);
2269:   }
2270: 
2271:   /**
2272:    * Returns a string, used only for debugging, which identifies or somehow
2273:    * represents this button. The exact value is implementation-defined.
2274:    *
2275:    * @return A string representation of the button
2276:    */
2277:   protected String paramString()
2278:   {
2279:     StringBuffer sb = new StringBuffer();
2280:     sb.append(super.paramString());
2281:     sb.append(",defaultIcon=");
2282:     if (getIcon() != null)
2283:       sb.append(getIcon());
2284:     sb.append(",disabledIcon=");
2285:     if (getDisabledIcon() != null)
2286:       sb.append(getDisabledIcon());
2287:     sb.append(",disabledSelectedIcon=");
2288:     if (getDisabledSelectedIcon() != null)
2289:       sb.append(getDisabledSelectedIcon());
2290:     sb.append(",margin=");
2291:     if (getMargin() != null)
2292:       sb.append(getMargin());
2293:     sb.append(",paintBorder=").append(isBorderPainted());
2294:     sb.append(",paintFocus=").append(isFocusPainted());
2295:     sb.append(",pressedIcon=");
2296:     if (getPressedIcon() != null)
2297:       sb.append(getPressedIcon());
2298:     sb.append(",rolloverEnabled=").append(isRolloverEnabled());
2299:     sb.append(",rolloverIcon=");
2300:     if (getRolloverIcon() != null)
2301:       sb.append(getRolloverIcon());
2302:     sb.append(",rolloverSelected=");
2303:     if (getRolloverSelectedIcon() != null)
2304:       sb.append(getRolloverSelectedIcon());
2305:     sb.append(",selectedIcon=");
2306:     if (getSelectedIcon() != null)
2307:       sb.append(getSelectedIcon());
2308:     sb.append(",text=");
2309:     if (getText() != null)
2310:       sb.append(getText());
2311:     return sb.toString();
2312:   }
2313: 
2314:   /**
2315:    * Set the "UI" property of the button, which is a look and feel class
2316:    * responsible for handling the button's input events and painting it.
2317:    *
2318:    * @param ui The new "UI" property
2319:    */
2320:   public void setUI(ButtonUI ui)
2321:   {
2322:     super.setUI(ui);
2323:   }
2324:   
2325:   /**
2326:    * Set the "UI" property of the button, which is a look and feel class
2327:    * responsible for handling the button's input events and painting it.
2328:    *
2329:    * @return The current "UI" property
2330:    */
2331:   public ButtonUI getUI()
2332:   {
2333:     return (ButtonUI) ui;
2334:   }
2335:   
2336:   /**
2337:    * Set the "UI" property to a class constructed, via the {@link
2338:    * UIManager}, from the current look and feel. This should be overridden
2339:    * for each subclass of AbstractButton, to retrieve a suitable {@link
2340:    * ButtonUI} look and feel class.
2341:    */
2342:   public void updateUI()
2343:   {
2344:     // TODO: What to do here?
2345:   }
2346: 
2347:   /**
2348:    * Returns the current time in milliseconds in which clicks gets coalesced
2349:    * into a single <code>ActionEvent</code>.
2350:    *
2351:    * @return the time in milliseconds
2352:    * 
2353:    * @since 1.4
2354:    */
2355:   public long getMultiClickThreshhold()
2356:   {
2357:     return multiClickThreshhold;
2358:   }
2359: 
2360:   /**
2361:    * Sets the time in milliseconds in which clicks gets coalesced into a single
2362:    * <code>ActionEvent</code>.
2363:    *
2364:    * @param threshhold the time in milliseconds
2365:    * 
2366:    * @since 1.4
2367:    */
2368:   public void setMultiClickThreshhold(long threshhold)
2369:   {
2370:     if (threshhold < 0)
2371:       throw new IllegalArgumentException();
2372: 
2373:     multiClickThreshhold = threshhold;
2374:   }
2375: 
2376:   /**
2377:    * Adds the specified component to this AbstractButton. This overrides the
2378:    * default in order to install an {@link OverlayLayout} layout manager
2379:    * before adding the component. The layout manager is only installed if
2380:    * no other layout manager has been installed before.
2381:    *
2382:    * @param comp the component to be added
2383:    * @param constraints constraints for the layout manager
2384:    * @param index the index at which the component is added
2385:    *
2386:    * @since 1.5
2387:    */
2388:   protected void addImpl(Component comp, Object constraints, int index)
2389:   {
2390:     // We use a client property here, so that no extra memory is used in
2391:     // the common case with no layout manager.
2392:     if (getClientProperty("AbstractButton.customLayoutSet") == null)
2393:       setLayout(new OverlayLayout(this));
2394:     super.addImpl(comp, constraints, index);
2395:   }
2396: 
2397:   /**
2398:    * Sets a layout manager on this AbstractButton. This is overridden in order
2399:    * to detect if the application sets a custom layout manager. If no custom
2400:    * layout manager is set, {@link #addImpl(Component, Object, int)} installs
2401:    * an OverlayLayout before adding a component.
2402:    *
2403:    * @param layout the layout manager to install
2404:    *
2405:    * @since 1.5
2406:    */
2407:   public void setLayout(LayoutManager layout)
2408:   {
2409:     // We use a client property here, so that no extra memory is used in
2410:     // the common case with no layout manager.
2411:     putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE);
2412:     super.setLayout(layout);
2413:   }
2414: 
2415:   /**
2416:    * Helper method for
2417:    * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
2418:    * 
2419:    * @param propertyName the name of the property
2420:    * @param value the value of the property
2421:    *
2422:    * @throws IllegalArgumentException if the specified property cannot be set
2423:    *         by this method
2424:    * @throws ClassCastException if the property value does not match the
2425:    *         property type
2426:    * @throws NullPointerException if <code>c</code> or
2427:    *         <code>propertyValue</code> is <code>null</code>
2428:    */
2429:   void setUIProperty(String propertyName, Object value)
2430:   {
2431:     if (propertyName.equals("borderPainted"))
2432:       {
2433:         if (! clientBorderPaintedSet)
2434:           {
2435:             setBorderPainted(((Boolean) value).booleanValue());
2436:             clientBorderPaintedSet = false;
2437:           }
2438:       }
2439:     else if (propertyName.equals("rolloverEnabled"))
2440:       {
2441:         if (! clientRolloverEnabledSet)
2442:           {
2443:             setRolloverEnabled(((Boolean) value).booleanValue());
2444:             clientRolloverEnabledSet = false;
2445:           }
2446:       }
2447:     else if (propertyName.equals("iconTextGap"))
2448:       {
2449:         if (! clientIconTextGapSet)
2450:           {
2451:             setIconTextGap(((Integer) value).intValue());
2452:             clientIconTextGapSet = false;
2453:           }
2454:       }
2455:     else if (propertyName.equals("contentAreaFilled"))
2456:       {
2457:         if (! clientContentAreaFilledSet)
2458:           {
2459:             setContentAreaFilled(((Boolean) value).booleanValue());
2460:             clientContentAreaFilledSet = false;
2461:           }
2462:       }
2463:     else
2464:       {
2465:         super.setUIProperty(propertyName, value);
2466:       }
2467:   }
2468: }