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