OSDN Git Service

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