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