OSDN Git Service

2006-06-09 Thomas Fitzsimmons <fitzsim@redhat.com>
[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 disabeldIcon;
203
204   /** The icon displayed when the button is selected. */
205   Icon selectedIcon;
206
207   /** The icon displayed when the button is selected but disabled. */
208   Icon disabledSelectedIcon;
209
210   /** The icon displayed when the button is rolled over. */
211   Icon rolloverIcon;
212
213   /** The icon displayed when the button is selected and rolled over. */
214   Icon rolloverSelectedIcon;
215
216   /** The icon currently displayed. */
217   Icon current_icon;
218
219   /** The text displayed in the button. */
220   String text;
221
222   /**
223    * The gap between icon and text, if both icon and text are
224    * non-<code>null</code>.
225    */
226   int iconTextGap;
227
228   /** The vertical alignment of the button's text and icon. */
229   int verticalAlignment;
230
231   /** The horizontal alignment of the button's text and icon. */
232   int horizontalAlignment;
233
234   /** The horizontal position of the button's text relative to its icon. */
235   int horizontalTextPosition;
236
237   /** The vertical position of the button's text relative to its icon. */
238   int verticalTextPosition;
239
240   /** Whether or not the button paints its border. */
241   boolean borderPainted;
242
243   /** Whether or not the button paints its focus state. */
244   boolean focusPainted;
245
246   /** Whether or not the button fills its content area. */
247   boolean contentAreaFilled;
248   
249   /** Whether rollover is enabled. */
250   boolean rollOverEnabled;
251
252   /** The action taken when the button is clicked. */
253   Action action;
254
255   /** The button's current state. */
256   protected ButtonModel model;
257
258   /** The margin between the button's border and its label. */
259   Insets margin;
260
261   /**
262    * A hint to the look and feel class, suggesting which character in the
263    * button's label should be underlined when drawing the label.
264    */
265   int mnemonicIndex;
266
267   /** Listener the button uses to receive ActionEvents from its model.  */
268   protected ActionListener actionListener;
269
270   /** Listener the button uses to receive ItemEvents from its model.  */
271   protected ItemListener itemListener;
272
273   /** Listener the button uses to receive ChangeEvents from its model.  */  
274   protected ChangeListener changeListener;
275
276   /**
277    * The time in 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         this.text = text;
927
928     if (icon != null)
929       default_icon = icon;
930  }
931  
932   /**
933    * <p>Returns the action command string for this button's model.</p>
934    *
935    * <p>If the action command was set to <code>null</code>, the button's
936    * text (label) is returned instead.</p>
937    *
938    * @return The current action command string from the button's model
939    */
940   public String getActionCommand()
941   {
942     String ac = model.getActionCommand();
943     if (ac != null)
944       return ac;
945     else
946       return text;
947   }
948
949   /**
950    * Sets the action command string for this button's model.
951    *
952    * @param actionCommand The new action command string to set in the button's
953    * model.
954    */
955   public void setActionCommand(String actionCommand)
956   {
957     if (model != null)
958       model.setActionCommand(actionCommand);
959   }
960
961   /**
962    * Adds an ActionListener to the button's listener list. When the
963    * button's model is clicked it fires an ActionEvent, and these
964    * listeners will be called.
965    *
966    * @param l The new listener to add
967    */
968   public void addActionListener(ActionListener l)
969   {
970     listenerList.add(ActionListener.class, l);
971   }
972
973   /**
974    * Removes an ActionListener from the button's listener list.
975    *
976    * @param l The listener to remove
977    */
978   public void removeActionListener(ActionListener l)
979   {
980     listenerList.remove(ActionListener.class, l);
981   }
982
983   /**
984    * Returns all added <code>ActionListener</code> objects.
985    * 
986    * @return an array of listeners
987    * 
988    * @since 1.4
989    */
990   public ActionListener[] getActionListeners()
991   {
992     return (ActionListener[]) listenerList.getListeners(ActionListener.class);
993   }
994
995   /**
996    * Adds an ItemListener to the button's listener list. When the button's
997    * model changes state (between any of ARMED, ENABLED, PRESSED, ROLLOVER
998    * or SELECTED) it fires an ItemEvent, and these listeners will be
999    * called.
1000    *
1001    * @param l The new listener to add
1002    */
1003   public void addItemListener(ItemListener l)
1004   {
1005     listenerList.add(ItemListener.class, l);
1006   }
1007
1008   /**
1009    * Removes an ItemListener from the button's listener list.
1010    *
1011    * @param l The listener to remove
1012    */
1013   public void removeItemListener(ItemListener l)
1014   {
1015     listenerList.remove(ItemListener.class, l);
1016   }
1017
1018   /**
1019    * Returns all added <code>ItemListener</code> objects.
1020    * 
1021    * @return an array of listeners
1022    * 
1023    * @since 1.4
1024    */
1025   public ItemListener[] getItemListeners()
1026   {
1027     return (ItemListener[]) listenerList.getListeners(ItemListener.class);
1028   }
1029
1030   /**
1031    * Adds a ChangeListener to the button's listener list. When the button's
1032    * model changes any of its (non-bound) properties, these listeners will be
1033    * called. 
1034    *
1035    * @param l The new listener to add
1036    */
1037   public void addChangeListener(ChangeListener l)
1038   {
1039     listenerList.add(ChangeListener.class, l);
1040   }
1041
1042   /**
1043    * Removes a ChangeListener from the button's listener list.
1044    *
1045    * @param l The listener to remove
1046    */
1047   public void removeChangeListener(ChangeListener l)
1048   {
1049     listenerList.remove(ChangeListener.class, l);
1050   }
1051
1052   /**
1053    * Returns all added <code>ChangeListener</code> objects.
1054    * 
1055    * @return an array of listeners
1056    * 
1057    * @since 1.4
1058    */
1059   public ChangeListener[] getChangeListeners()
1060   {
1061     return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
1062   }
1063
1064   /**
1065    * Calls {@link ItemListener#itemStateChanged} on each ItemListener in
1066    * the button's listener list.
1067    *
1068    * @param e The event signifying that the button's model changed state
1069    */
1070   protected void fireItemStateChanged(ItemEvent e)
1071   {
1072     e.setSource(this);
1073     ItemListener[] listeners = getItemListeners();
1074  
1075     for (int i = 0; i < listeners.length; i++)
1076       listeners[i].itemStateChanged(e);
1077   }
1078
1079   /**
1080    * Calls {@link ActionListener#actionPerformed} on each {@link
1081    * ActionListener} in the button's listener list.
1082    *
1083    * @param e The event signifying that the button's model was clicked
1084    */
1085   protected void fireActionPerformed(ActionEvent e)
1086   {
1087         // Dispatch a copy of the given ActionEvent in order to
1088         // set the source and action command correctly.
1089     ActionEvent ae = new ActionEvent(
1090         this,
1091         e.getID(),
1092         getActionCommand(),
1093         e.getWhen(),
1094         e.getModifiers());
1095
1096     ActionListener[] listeners = getActionListeners();
1097     
1098     for (int i = 0; i < listeners.length; i++)
1099       listeners[i].actionPerformed(ae);
1100   }
1101
1102   /**
1103    * Calls {@link ChangeListener#stateChanged} on each {@link ChangeListener}
1104    * in the button's listener list.
1105    */
1106   protected void fireStateChanged()
1107   {
1108     ChangeListener[] listeners = getChangeListeners();
1109
1110     for (int i = 0; i < listeners.length; i++)
1111       listeners[i].stateChanged(changeEvent);
1112   }
1113
1114   /**
1115    * Get the current keyboard mnemonic value. This value corresponds to a
1116    * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1117    * codes) and is used to activate the button when pressed in conjunction
1118    * with the "mouseless modifier" of the button's look and feel class, and
1119    * when focus is in one of the button's ancestors.
1120    *
1121    * @return The button's current keyboard mnemonic
1122    */
1123   public int getMnemonic()
1124   {
1125     ButtonModel mod = getModel();
1126     if (mod != null)
1127       return mod.getMnemonic();
1128     return -1;
1129   }
1130
1131   /**
1132    * Set the current keyboard mnemonic value. This value corresponds to a
1133    * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1134    * codes) and is used to activate the button when pressed in conjunction
1135    * with the "mouseless modifier" of the button's look and feel class, and
1136    * when focus is in one of the button's ancestors.
1137    *
1138    * @param mne A new mnemonic to use for the button
1139    */
1140   public void setMnemonic(char mne)
1141   {
1142     setMnemonic((int) mne);
1143   }
1144
1145   /**
1146    * Set the current keyboard mnemonic value. This value corresponds to a
1147    * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
1148    * codes) and is used to activate the button when pressed in conjunction
1149    * with the "mouseless modifier" of the button's look and feel class, and
1150    * when focus is in one of the button's ancestors.
1151    *
1152    * @param mne A new mnemonic to use for the button
1153    */
1154   public void setMnemonic(int mne)
1155   {
1156     ButtonModel mod = getModel();
1157     int old = -1;
1158     if (mod != null)
1159       old = mod.getMnemonic();
1160
1161     if (old != mne)
1162       {
1163         if (mod != null)
1164           mod.setMnemonic(mne);
1165
1166         if (text != null && !text.equals(""))
1167           {
1168             // Since lower case char = upper case char for
1169             // mnemonic, we will convert both text and mnemonic
1170             // to upper case before checking if mnemonic character occurs
1171             // in the menu item text.
1172             int upperCaseMne = Character.toUpperCase((char) mne);
1173             String upperCaseText = text.toUpperCase();
1174             setDisplayedMnemonicIndex(upperCaseText.indexOf(upperCaseMne));
1175           }
1176
1177         firePropertyChange(MNEMONIC_CHANGED_PROPERTY, old, mne);
1178         revalidate();
1179         repaint();
1180       }
1181   }
1182
1183   /** 
1184    * Sets the button's mnemonic index. The mnemonic index is a hint to the
1185    * look and feel class, suggesting which character in the button's label
1186    * should be underlined when drawing the label. If the mnemonic index is
1187    * -1, no mnemonic will be displayed. 
1188    * 
1189    * If no mnemonic index is set, the button will choose a mnemonic index
1190    * by default, which will be the first occurrence of the mnemonic
1191    * character in the button's text.
1192    *
1193    * @param index An offset into the "text" property of the button
1194    * @throws IllegalArgumentException If <code>index</code> is not within the
1195    * range of legal offsets for the "text" property of the button.
1196    * @since 1.4
1197    */
1198
1199   public void setDisplayedMnemonicIndex(int index)
1200   {
1201     if (index < -1 || (text != null && index >= text.length()))
1202       throw new IllegalArgumentException();
1203   
1204     mnemonicIndex = index;
1205   }
1206   
1207   /** 
1208    * Get the button's mnemonic index, which is an offset into the button's
1209    * "text" property.  The character specified by this offset should be
1210    * underlined when the look and feel class draws this button.
1211    *
1212    * @return An index into the button's "text" property
1213    */
1214   public int getDisplayedMnemonicIndex()
1215   {
1216     return mnemonicIndex;
1217   }
1218   
1219
1220   /**
1221    * Set the "rolloverEnabled" property. When rollover is enabled, and the
1222    * look and feel supports it, the button will change its icon to
1223    * rolloverIcon, when the mouse passes over it.
1224    *
1225    * @param r Whether or not to enable rollover icon changes
1226    */
1227   public void setRolloverEnabled(boolean r)
1228   {
1229     clientRolloverEnabledSet = true;
1230     if (rollOverEnabled != r)
1231       {
1232         rollOverEnabled = r;
1233         firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r);
1234         revalidate();
1235         repaint();
1236       }
1237   }
1238
1239   /**
1240    * Returns whether or not rollover icon changes are enabled on the
1241    * button.
1242    *
1243    * @return The state of the "rolloverEnabled" property
1244    */
1245   public boolean isRolloverEnabled()
1246   {
1247     return rollOverEnabled;
1248   }
1249
1250   /**
1251    * Set the value of the button's "selected" property. Selection is only
1252    * meaningful for toggle-type buttons (check boxes, radio buttons).
1253    *
1254    * @param s New value for the property
1255    */
1256   public void setSelected(boolean s)
1257   {
1258     ButtonModel mod = getModel();
1259     if (mod != null)
1260       mod.setSelected(s);
1261   }
1262
1263   /**
1264    * Get the value of the button's "selected" property. Selection is only
1265    * meaningful for toggle-type buttons (check boxes, radio buttons).
1266    *
1267    * @return The value of the property
1268    */
1269   public boolean isSelected()
1270   {
1271     ButtonModel mod = getModel();
1272     if (mod != null)
1273       return mod.isSelected();
1274     return false;
1275   }
1276
1277   /**
1278    * Enables or disables the button. A button will neither be selectable
1279    * nor preform any actions unless it is enabled.
1280    *
1281    * @param b Whether or not to enable the button
1282    */
1283   public void setEnabled(boolean b)
1284   {
1285     // Do nothing if state does not change.
1286     if (b == isEnabled())
1287       return;
1288     super.setEnabled(b);
1289     setFocusable(b);
1290     ButtonModel mod = getModel();
1291     if (mod != null)
1292       mod.setEnabled(b);
1293   }
1294
1295   /** 
1296    * Set the horizontal alignment of the button's text and icon. The
1297    * alignment is a numeric constant from {@link SwingConstants}. It must
1298    * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1299    * <code>LEADING</code> or <code>TRAILING</code>.  The default is
1300    * <code>RIGHT</code>.
1301    * 
1302    * @return The current horizontal alignment
1303    */
1304   public int getHorizontalAlignment()
1305   {
1306     return horizontalAlignment;
1307   }
1308
1309   /**
1310    * Set the horizontal alignment of the button's text and icon. The
1311    * alignment is a numeric constant from {@link SwingConstants}. It must
1312    * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1313    * <code>LEADING</code> or <code>TRAILING</code>.  The default is
1314    * <code>RIGHT</code>.
1315    *
1316    * @param a The new horizontal alignment
1317    * @throws IllegalArgumentException If alignment is not one of the legal
1318    * constants.
1319    */
1320   public void setHorizontalAlignment(int a)
1321   {
1322     if (horizontalAlignment == a)
1323       return;
1324
1325     int old = horizontalAlignment;
1326     horizontalAlignment = a;
1327     firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1328     revalidate();
1329     repaint();
1330   }
1331
1332   /**
1333    * Get the horizontal position of the button's text relative to its
1334    * icon. The position is a numeric constant from {@link
1335    * SwingConstants}. It must be one of: <code>RIGHT</code>,
1336    * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1337    * <code>TRAILING</code>.  The default is <code>TRAILING</code>.
1338    *
1339    * @return The current horizontal text position
1340    */
1341   public int getHorizontalTextPosition()
1342   {
1343     return horizontalTextPosition;
1344   }
1345
1346   /**
1347    * Set the horizontal position of the button's text relative to its
1348    * icon. The position is a numeric constant from {@link
1349    * SwingConstants}. It must be one of: <code>RIGHT</code>,
1350    * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
1351    * <code>TRAILING</code>. The default is <code>TRAILING</code>.
1352    *
1353    * @param t The new horizontal text position
1354    * @throws IllegalArgumentException If position is not one of the legal
1355    * constants.
1356    */
1357   public void setHorizontalTextPosition(int t)
1358   {
1359     if (horizontalTextPosition == t)
1360       return;
1361
1362     int old = horizontalTextPosition;
1363     horizontalTextPosition = t;
1364     firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1365     revalidate();
1366     repaint();
1367   }
1368
1369   /**
1370    * Get the vertical alignment of the button's text and icon. The
1371    * alignment is a numeric constant from {@link SwingConstants}. It must
1372    * be one of: <code>CENTER</code>, <code>TOP</code>, or
1373    * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1374    *
1375    * @return The current vertical alignment
1376    */
1377   public int getVerticalAlignment()
1378   {
1379     return verticalAlignment;
1380   }
1381
1382   /**
1383    * Set the vertical alignment of the button's text and icon. The
1384    * alignment is a numeric constant from {@link SwingConstants}. It must
1385    * be one of: <code>CENTER</code>, <code>TOP</code>, or
1386    * <code>BOTTOM</code>. The default is <code>CENTER</code>.
1387    *
1388    * @param a The new vertical alignment
1389    * @throws IllegalArgumentException If alignment is not one of the legal
1390    * constants.
1391    */
1392   public void setVerticalAlignment(int a)
1393   {
1394     if (verticalAlignment == a)
1395       return;
1396     
1397     int old = verticalAlignment;
1398     verticalAlignment = a;
1399     firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
1400     revalidate();
1401     repaint();
1402   }
1403
1404   /**
1405    * Get the vertical position of the button's text relative to its
1406    * icon. The alignment is a numeric constant from {@link
1407    * SwingConstants}. It must be one of: <code>CENTER</code>,
1408    * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1409    * <code>CENTER</code>.
1410    *
1411    * @return The current vertical position
1412    */
1413   public int getVerticalTextPosition()
1414   {
1415     return verticalTextPosition;
1416   }
1417
1418   /**
1419    * Set the vertical position of the button's text relative to its
1420    * icon. The alignment is a numeric constant from {@link
1421    * SwingConstants}. It must be one of: <code>CENTER</code>,
1422    * <code>TOP</code>, or <code>BOTTOM</code>. The default is
1423    * <code>CENTER</code>.
1424    *
1425    * @param t The new vertical position
1426    * @throws IllegalArgumentException If position is not one of the legal
1427    * constants.
1428    */
1429   public void setVerticalTextPosition(int t)
1430   {
1431     if (verticalTextPosition == t)
1432       return;
1433     
1434     int old = verticalTextPosition;
1435     verticalTextPosition = t;
1436     firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
1437     revalidate();
1438     repaint();
1439   }
1440
1441   /**
1442    * Set the value of the "borderPainted" property. If set to
1443    * <code>false</code>, the button's look and feel class should not paint
1444    * a border for the button. The default is <code>true</code>.
1445    *
1446    * @return The current value of the property.
1447    */
1448   public boolean isBorderPainted()
1449   {
1450     return borderPainted;
1451   }
1452
1453   /**
1454    * Set the value of the "borderPainted" property. If set to
1455    * <code>false</code>, the button's look and feel class should not paint
1456    * a border for the button. The default is <code>true</code>.
1457    *
1458    * @param b The new value of the property.
1459    */
1460   public void setBorderPainted(boolean b)
1461   {
1462     clientBorderPaintedSet = true;
1463     if (borderPainted == b)
1464       return;
1465     boolean old = borderPainted;
1466     borderPainted = b;
1467     firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
1468     revalidate();
1469     repaint();
1470   }
1471
1472   /**
1473    * Get the value of the "action" property. 
1474    *
1475    * @return The current value of the "action" property
1476    */
1477   public Action getAction()
1478   {
1479     return action;
1480   }
1481
1482   /**
1483    * <p>Set the button's "action" property, subscribing the new action to the
1484    * button, as an ActionListener, if it is not already subscribed. The old
1485    * Action, if it exists, is unsubscribed, and the button is unsubscribed
1486    * from the old Action if it was previously subscribed as a
1487    * PropertyChangeListener.</p>
1488    *
1489    * <p>This method also configures several of the button's properties from
1490    * the Action, by calling {@link #configurePropertiesFromAction}, and
1491    * subscribes the button to the Action as a PropertyChangeListener.
1492    * Subsequent changes to the Action will thus reconfigure the button 
1493    * automatically.</p>
1494    *
1495    * @param a The new value of the "action" property
1496    */
1497   public void setAction(Action a)
1498   {
1499     if (action != null)
1500       {
1501         action.removePropertyChangeListener(actionPropertyChangeListener);
1502         removeActionListener(action);
1503         if (actionPropertyChangeListener != null)
1504           {
1505             action.removePropertyChangeListener(actionPropertyChangeListener);
1506             actionPropertyChangeListener = null;
1507           }
1508       }
1509
1510     Action old = action;
1511     action = a;
1512     configurePropertiesFromAction(action);
1513     if (action != null)
1514       {
1515         actionPropertyChangeListener = createActionPropertyChangeListener(a);      
1516         action.addPropertyChangeListener(actionPropertyChangeListener);
1517         addActionListener(action);
1518       }
1519   }
1520
1521   /**
1522    * Return the button's default "icon" property.
1523    *
1524    * @return The current default icon
1525    */
1526   public Icon getIcon()
1527   {
1528     return default_icon;
1529   }
1530
1531   /**
1532    * Set the button's default "icon" property. This icon is used as a basis
1533    * for the pressed and disabled icons, if none are explicitly set.
1534    *
1535    * @param i The new default icon
1536    */
1537   public void setIcon(Icon i)
1538   {
1539     if (default_icon == i)
1540       return;
1541     
1542     Icon old = default_icon;      
1543     default_icon = i;      
1544     firePropertyChange(ICON_CHANGED_PROPERTY, old, i);
1545     revalidate();
1546     repaint();
1547   }
1548
1549   /**
1550    * Return the button's "text" property. This property is synonymous with
1551    * the "label" property.
1552    *
1553    * @return The current "text" property
1554    */
1555   public String getText()
1556   {
1557     return text;
1558   }
1559
1560   /**
1561    * Set the button's "label" property. This property is synonymous with the
1562    * "text" property.
1563    *
1564    * @param label The new "label" property
1565    *
1566    * @deprecated use <code>setText(text)</code>
1567    */
1568   public void setLabel(String label)
1569   {
1570     setText(label);
1571   }
1572
1573   /**
1574    * Return the button's "label" property. This property is synonymous with
1575    * the "text" property.
1576    *
1577    * @return The current "label" property
1578    *
1579    * @deprecated use <code>getText()</code>
1580    */
1581   public String getLabel()
1582   {
1583     return getText();
1584   }
1585
1586   /**
1587    * Set the button's "text" property. This property is synonymous with the
1588    * "label" property.
1589    *
1590    * @param t The new "text" property
1591    */
1592   public void setText(String t)
1593   {
1594     if (text == t)
1595       return;
1596     
1597     String old = text;
1598     text = t;
1599     firePropertyChange(TEXT_CHANGED_PROPERTY, old, t);
1600     revalidate();
1601     repaint();
1602   }
1603
1604   /**
1605    * Set the value of the {@link #iconTextGap} property.
1606    * 
1607    * @param i The new value of the property
1608    * 
1609    * @since 1.4
1610    */
1611   public void setIconTextGap(int i)
1612   {
1613     clientIconTextGapSet = true;
1614     if (iconTextGap == i)
1615       return;
1616     
1617     int old = iconTextGap;
1618     iconTextGap = i;
1619     firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
1620     revalidate();
1621     repaint();
1622   }
1623
1624   /**
1625    * Get the value of the {@link #iconTextGap} property.
1626    *
1627    * @return The current value of the property
1628    * 
1629    * @since 1.4
1630    */
1631   public int getIconTextGap()
1632   {
1633     return iconTextGap;
1634   }
1635
1636   /**
1637    * Return the button's "margin" property, which is an {@link Insets} object
1638    * describing the distance between the button's border and its text and
1639    * icon.
1640    *
1641    * @return The current "margin" property
1642    */
1643   public Insets getMargin()
1644   {
1645     return margin;
1646   }
1647
1648   /**
1649    * Set the button's "margin" property, which is an {@link Insets} object
1650    * describing the distance between the button's border and its text and
1651    * icon.
1652    *
1653    * @param m The new "margin" property
1654    */
1655   public void setMargin(Insets m)
1656   {
1657     if (margin == m)
1658       return;
1659     
1660     Insets old = margin;
1661     margin = m;
1662     firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
1663     revalidate();
1664     repaint();
1665   }
1666
1667   /**
1668    * Return the button's "pressedIcon" property. The look and feel class
1669    * should paint this icon when the "pressed" property of the button's
1670    * {@link ButtonModel} is <code>true</code>. This property may be
1671    * <code>null</code>, in which case the default icon is used.
1672    *
1673    * @return The current "pressedIcon" property
1674    */
1675   public Icon getPressedIcon()
1676   {
1677     return pressed_icon;
1678   }
1679
1680   /**
1681    * Set the button's "pressedIcon" property. The look and feel class
1682    * should paint this icon when the "pressed" property of the button's
1683    * {@link ButtonModel} is <code>true</code>. This property may be
1684    * <code>null</code>, in which case the default icon is used.
1685    *
1686    * @param pressedIcon The new "pressedIcon" property
1687    */
1688   public void setPressedIcon(Icon pressedIcon)
1689   {
1690     if (pressed_icon == pressedIcon)
1691       return;
1692     
1693     Icon old = pressed_icon;
1694     pressed_icon = pressedIcon;
1695     firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon);
1696     revalidate();
1697     repaint();
1698   }
1699
1700   /**
1701    * Return the button's "disabledIcon" property. The look and feel class
1702    * should paint this icon when the "enabled" property of the button's
1703    * {@link ButtonModel} is <code>false</code>. This property may be
1704    * <code>null</code>, in which case an icon is constructed, based on the
1705    * default icon.
1706    *
1707    * @return The current "disabledIcon" property
1708    */
1709   public Icon getDisabledIcon()
1710   {
1711     if (disabeldIcon == null && default_icon instanceof ImageIcon)
1712       {
1713         Image iconImage = ((ImageIcon) default_icon).getImage();
1714         Image grayImage = GrayFilter.createDisabledImage(iconImage);
1715         disabeldIcon = new ImageIcon(grayImage);
1716       }
1717       
1718     return disabeldIcon;
1719   }
1720
1721   /**
1722    * Set the button's "disabledIcon" property. The look and feel class should
1723    * paint this icon when the "enabled" property of the button's {@link
1724    * ButtonModel} is <code>false</code>. This property may be
1725    * <code>null</code>, in which case an icon is constructed, based on the
1726    * default icon.
1727    *
1728    * @param d The new "disabledIcon" property
1729    */
1730   public void setDisabledIcon(Icon d)
1731   {
1732     disabeldIcon = d;
1733     revalidate();
1734     repaint();
1735   }
1736
1737   /**
1738    * Return the button's "paintFocus" property. This property controls
1739    * whether or not the look and feel class will paint a special indicator
1740    * of focus state for the button. If it is false, the button still paints
1741    * when focused, but no special decoration is painted to indicate the
1742    * presence of focus.
1743    *
1744    * @return The current "paintFocus" property
1745    */
1746   public boolean isFocusPainted()
1747   {
1748     return focusPainted;
1749   }
1750
1751   /**
1752    * Set the button's "paintFocus" property. This property controls whether
1753    * or not the look and feel class will paint a special indicator of focus
1754    * state for the button. If it is false, the button still paints when
1755    * focused, but no special decoration is painted to indicate the presence
1756    * of focus.
1757    *
1758    * @param p The new "paintFocus" property
1759    */
1760   public void setFocusPainted(boolean p)
1761   {
1762     if (focusPainted == p)
1763       return;
1764     
1765     boolean old = focusPainted;
1766     focusPainted = p;
1767     firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p);
1768     revalidate();
1769     repaint();
1770   }
1771
1772   /**
1773    * Verifies that a particular key is one of the valid constants used for
1774    * describing horizontal alignment and positioning. The valid constants
1775    * are the following members of {@link SwingConstants}:
1776    * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
1777    * <code>LEADING</code> or <code>TRAILING</code>.
1778    *
1779    * @param key The key to check
1780    * @param exception A message to include in an IllegalArgumentException
1781    *
1782    * @return the value of key
1783    *
1784    * @throws IllegalArgumentException If key is not one of the valid constants
1785    *
1786    * @see #setHorizontalTextPosition(int)
1787    * @see #setHorizontalAlignment(int)
1788    */
1789   protected  int checkHorizontalKey(int key, String exception)
1790   {
1791     switch (key)
1792       {
1793       case SwingConstants.RIGHT:
1794       case SwingConstants.LEFT:
1795       case SwingConstants.CENTER:
1796       case SwingConstants.LEADING:
1797       case SwingConstants.TRAILING:
1798         break;
1799       default:
1800         throw new IllegalArgumentException(exception);
1801       }
1802     return key;
1803   }
1804
1805   /**
1806    * Verifies that a particular key is one of the valid constants used for
1807    * describing vertical alignment and positioning. The valid constants are
1808    * the following members of {@link SwingConstants}: <code>TOP</code>,
1809    * <code>BOTTOM</code> or <code>CENTER</code>.
1810    *
1811    * @param key The key to check
1812    * @param exception A message to include in an IllegalArgumentException
1813    *
1814    * @return the value of key
1815    *
1816    * @throws IllegalArgumentException If key is not one of the valid constants
1817    *
1818    * @see #setVerticalTextPosition(int)
1819    * @see #setVerticalAlignment(int)
1820    */
1821   protected  int checkVerticalKey(int key, String exception)
1822   {
1823     switch (key)
1824       {
1825       case SwingConstants.TOP:
1826       case SwingConstants.BOTTOM:
1827       case SwingConstants.CENTER:
1828         break;
1829       default:
1830         throw new IllegalArgumentException(exception);
1831       }
1832     return key;
1833   }
1834
1835   /**
1836    * Configure various properties of the button by reading properties
1837    * of an {@link Action}. The mapping of properties is as follows:
1838    *
1839    * <table>
1840    *
1841    * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr>
1842    *
1843    * <tr><td>NAME                 </td> <td>text                   </td></tr>
1844    * <tr><td>SMALL_ICON           </td> <td>icon                   </td></tr>
1845    * <tr><td>SHORT_DESCRIPTION    </td> <td>toolTipText            </td></tr>
1846    * <tr><td>MNEMONIC_KEY         </td> <td>mnemonic               </td></tr>
1847    * <tr><td>ACTION_COMMAND_KEY   </td> <td>actionCommand          </td></tr>
1848    *
1849    * </table>
1850    *
1851    * <p>In addition, this method always sets the button's "enabled" property to
1852    * the value of the Action's "enabled" property.</p>
1853    *
1854    * <p>If the provided Action is <code>null</code>, the text, icon, and
1855    * toolTipText properties of the button are set to <code>null</code>, and
1856    * the "enabled" property is set to <code>true</code>; the mnemonic and
1857    * actionCommand properties are unchanged.</p>
1858    *
1859    * @param a An Action to configure the button from
1860    */
1861   protected void configurePropertiesFromAction(Action a)
1862   {
1863     if (a == null)
1864       {
1865         setText(null);
1866         setIcon(null);
1867         setEnabled(true);
1868         setToolTipText(null);
1869       }
1870     else
1871       {
1872         setText((String) (a.getValue(Action.NAME)));
1873         setIcon((Icon) (a.getValue(Action.SMALL_ICON)));
1874         setEnabled(a.isEnabled());
1875         setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
1876         if (a.getValue(Action.MNEMONIC_KEY) != null)
1877           setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue());
1878         String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY));
1879
1880         // Set actionCommand to button's text by default if it is not specified
1881         if (actionCommand != null)
1882           setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY)));
1883         else
1884           setActionCommand(getText());
1885       }
1886   }
1887
1888   /**
1889    * <p>A factory method which should return an {@link ActionListener} that
1890    * propagates events from the button's {@link ButtonModel} to any of the
1891    * button's ActionListeners. By default, this is an inner class which
1892    * calls {@link AbstractButton#fireActionPerformed} with a modified copy
1893    * of the incoming model {@link ActionEvent}.</p>
1894    *
1895    * <p>The button calls this method during construction, stores the
1896    * resulting ActionListener in its <code>actionListener</code> member
1897    * field, and subscribes it to the button's model. If the button's model
1898    * is changed, this listener is unsubscribed from the old model and
1899    * subscribed to the new one.</p>
1900    *
1901    * @return A new ActionListener 
1902    */
1903   protected  ActionListener createActionListener()
1904   {
1905     return new ActionListener()
1906       {
1907         public void actionPerformed(ActionEvent e)
1908         {
1909           AbstractButton.this.fireActionPerformed(e);
1910         }
1911       };
1912   }
1913
1914   /**
1915    * <p>A factory method which should return a {@link PropertyChangeListener}
1916    * that accepts changes to the specified {@link Action} and reconfigure
1917    * the {@link AbstractButton}, by default using the {@link
1918    * #configurePropertiesFromAction} method.</p>
1919    *
1920    * <p>The button calls this method whenever a new Action is assigned to
1921    * the button's "action" property, via {@link #setAction}, and stores the
1922    * resulting PropertyChangeListener in its
1923    * <code>actionPropertyChangeListener</code> member field. The button
1924    * then subscribes the listener to the button's new action. If the
1925    * button's action is changed subsequently, the listener is unsubscribed
1926    * from the old action and subscribed to the new one.</p>
1927    *
1928    * @param a The Action which will be listened to, and which should be 
1929    * the same as the source of any PropertyChangeEvents received by the
1930    * new listener returned from this method.
1931    *
1932    * @return A new PropertyChangeListener
1933    */
1934   protected  PropertyChangeListener createActionPropertyChangeListener(Action a)
1935   {
1936     return new PropertyChangeListener()
1937       {
1938         public void propertyChange(PropertyChangeEvent e)
1939         {
1940           Action act = (Action) (e.getSource());
1941           if (e.getPropertyName().equals("enabled"))
1942             setEnabled(act.isEnabled());
1943           else if (e.getPropertyName().equals(Action.NAME))
1944             setText((String) (act.getValue(Action.NAME)));
1945           else if (e.getPropertyName().equals(Action.SMALL_ICON))
1946             setIcon((Icon) (act.getValue(Action.SMALL_ICON)));
1947           else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION))
1948             setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION)));
1949           else if (e.getPropertyName().equals(Action.MNEMONIC_KEY))
1950             if (act.getValue(Action.MNEMONIC_KEY) != null)
1951               setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY)))
1952                           .intValue());
1953           else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY))
1954             setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY)));
1955         }
1956       };
1957   }
1958
1959   /**
1960    * <p>Factory method which creates a {@link ChangeListener}, used to
1961    * subscribe to ChangeEvents from the button's model. Subclasses of
1962    * AbstractButton may wish to override the listener used to subscribe to
1963    * such ChangeEvents. By default, the listener just propagates the
1964    * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
1965    * AbstractButton#fireStateChanged} method.</p>
1966    *
1967    * <p>The button calls this method during construction, stores the
1968    * resulting ChangeListener in its <code>changeListener</code> member
1969    * field, and subscribes it to the button's model. If the button's model
1970    * is changed, this listener is unsubscribed from the old model and
1971    * subscribed to the new one.</p>
1972    *
1973    * @return The new ChangeListener
1974    */
1975   protected ChangeListener createChangeListener()
1976   {
1977     return new ButtonChangeListener();
1978   }
1979
1980   /**
1981    * <p>Factory method which creates a {@link ItemListener}, used to
1982    * subscribe to ItemEvents from the button's model. Subclasses of
1983    * AbstractButton may wish to override the listener used to subscribe to
1984    * such ItemEvents. By default, the listener just propagates the
1985    * {@link ItemEvent} to the button's ItemListeners, via the {@link
1986    * AbstractButton#fireItemStateChanged} method.</p>
1987    *
1988    * <p>The button calls this method during construction, stores the
1989    * resulting ItemListener in its <code>changeListener</code> member
1990    * field, and subscribes it to the button's model. If the button's model
1991    * is changed, this listener is unsubscribed from the old model and
1992    * subscribed to the new one.</p>
1993    *
1994    * <p>Note that ItemEvents are only generated from the button's model
1995    * when the model's <em>selected</em> property changes. If you want to
1996    * subscribe to other properties of the model, you must subscribe to
1997    * ChangeEvents.
1998    *
1999    * @return The new ItemListener
2000    */
2001   protected  ItemListener createItemListener()
2002   {
2003     return new ItemListener()
2004       {
2005         public void itemStateChanged(ItemEvent e)
2006         {
2007           AbstractButton.this.fireItemStateChanged(e);
2008         }
2009       };
2010   }
2011
2012   /**
2013    * Programmatically perform a "click" on the button: arming, pressing,
2014    * waiting, un-pressing, and disarming the model.
2015    */
2016   public void doClick()
2017   {
2018     doClick(100);
2019   }
2020
2021   /**
2022    * Programmatically perform a "click" on the button: arming, pressing,
2023    * waiting, un-pressing, and disarming the model.
2024    *
2025    * @param pressTime The number of milliseconds to wait in the pressed state
2026    */
2027   public void doClick(int pressTime)
2028   {
2029     ButtonModel mod = getModel();
2030     if (mod != null)
2031       {
2032         mod.setArmed(true);
2033         mod.setPressed(true);
2034         try
2035           {
2036             java.lang.Thread.sleep(pressTime);
2037           }
2038         catch (java.lang.InterruptedException e)
2039           {
2040             // probably harmless
2041           }
2042         mod.setPressed(false);
2043         mod.setArmed(false);
2044       }
2045   }
2046
2047   /**
2048    * Return the button's disabled selected icon. The look and feel class
2049    * should paint this icon when the "enabled" property of the button's model
2050    * is <code>false</code> and its "selected" property is
2051    * <code>true</code>. This icon can be <code>null</code>, in which case
2052    * it is synthesized from the button's selected icon.
2053    *
2054    * @return The current disabled selected icon
2055    */
2056   public Icon getDisabledSelectedIcon()
2057   {
2058     return disabledSelectedIcon;
2059   }
2060
2061   /**
2062    * Set the button's disabled selected icon. The look and feel class
2063    * should paint this icon when the "enabled" property of the button's model
2064    * is <code>false</code> and its "selected" property is
2065    * <code>true</code>. This icon can be <code>null</code>, in which case
2066    * it is synthesized from the button's selected icon.
2067    *
2068    * @param icon The new disabled selected icon
2069    */
2070   public void setDisabledSelectedIcon(Icon icon)
2071   {
2072     if (disabledSelectedIcon == icon)
2073       return;
2074     
2075     Icon old = disabledSelectedIcon;
2076     disabledSelectedIcon = icon;
2077     firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
2078     revalidate();
2079     repaint();        
2080   }
2081
2082   /**
2083    * Return the button's rollover icon. The look and feel class should
2084    * paint this icon when the "rolloverEnabled" property of the button is
2085    * <code>true</code> and the mouse rolls over the button.
2086    *
2087    * @return The current rollover icon
2088    */
2089   public Icon getRolloverIcon()
2090   {
2091     return rolloverIcon;
2092   }
2093
2094   /**
2095    * Set the button's rollover icon. The look and feel class should
2096    * paint this icon when the "rolloverEnabled" property of the button is
2097    * <code>true</code> and the mouse rolls over the button.
2098    *
2099    * @param r The new rollover icon
2100    */
2101   public void setRolloverIcon(Icon r)
2102   {
2103     if (rolloverIcon == r)
2104       return;
2105     
2106     Icon old = rolloverIcon;
2107     rolloverIcon = r;
2108     firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
2109     revalidate();
2110     repaint();
2111   }
2112
2113   /**
2114    * Return the button's rollover selected icon. The look and feel class
2115    * should paint this icon when the "rolloverEnabled" property of the button
2116    * is <code>true</code>, the "selected" property of the button's model is
2117    * <code>true</code>, and the mouse rolls over the button.
2118    *
2119    * @return The current rollover selected icon
2120    */
2121   public Icon getRolloverSelectedIcon()
2122   {
2123     return rolloverSelectedIcon;
2124   }
2125
2126   /**
2127    * Set the button's rollover selected icon. The look and feel class
2128    * should paint this icon when the "rolloverEnabled" property of the button
2129    * is <code>true</code>, the "selected" property of the button's model is
2130    * <code>true</code>, and the mouse rolls over the button.
2131    *
2132    * @param r The new rollover selected icon
2133    */
2134   public void setRolloverSelectedIcon(Icon r)
2135   {
2136     if (rolloverSelectedIcon == r)
2137       return;
2138     
2139     Icon old = rolloverSelectedIcon;
2140     rolloverSelectedIcon = r;
2141     firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
2142     revalidate();
2143     repaint();
2144   }
2145
2146   /**
2147    * Return the button's selected icon. The look and feel class should
2148    * paint this icon when the "selected" property of the button's model is
2149    * <code>true</code>, and either the "rolloverEnabled" property of the
2150    * button is <code>false</code> or the mouse is not currently rolled
2151    * over the button.
2152    *
2153    * @return The current selected icon
2154    */
2155   public Icon getSelectedIcon()
2156   {
2157     return selectedIcon;
2158   }
2159
2160   /**
2161    * Set the button's selected icon. The look and feel class should
2162    * paint this icon when the "selected" property of the button's model is
2163    * <code>true</code>, and either the "rolloverEnabled" property of the
2164    * button is <code>false</code> or the mouse is not currently rolled
2165    * over the button.
2166    *
2167    * @param s The new selected icon
2168    */
2169   public void setSelectedIcon(Icon s)
2170   {
2171     if (selectedIcon == s)
2172       return;
2173     
2174     Icon old = selectedIcon;
2175     selectedIcon = s;
2176     firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
2177     revalidate();
2178     repaint();
2179   }
2180
2181   /**
2182    * Returns an single-element array containing the "text" property of the
2183    * button if the "selected" property of the button's model is
2184    * <code>true</code>, otherwise returns <code>null</code>.
2185    *
2186    * @return The button's "selected object" array
2187    */
2188   public Object[] getSelectedObjects()
2189   {
2190     if (isSelected())
2191       {
2192         Object[] objs = new Object[1];
2193         objs[0] = getText();
2194         return objs;
2195       }
2196     else
2197       {
2198         return null;
2199       }
2200   }
2201
2202   /**
2203    * Called when image data becomes available for one of the button's icons.
2204    *
2205    * @param img The image being updated
2206    * @param infoflags One of the constant codes in {@link ImageObserver} used
2207    *     to describe updated portions of an image.
2208    * @param x X coordinate of the region being updated
2209    * @param y Y coordinate of the region being updated
2210    * @param w Width of the region beign updated
2211    * @param h Height of the region being updated
2212    *
2213    * @return <code>true</code> if img is equal to the button's current icon,
2214    *     otherwise <code>false</code>
2215    */
2216   public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
2217                              int h)
2218   {
2219     return current_icon == img;
2220   }
2221
2222   /**
2223    * Returns the value of the button's "contentAreaFilled" property. This
2224    * property indicates whether the area surrounding the text and icon of
2225    * the button should be filled by the look and feel class.  If this
2226    * property is <code>false</code>, the look and feel class should leave
2227    * the content area transparent.
2228    *
2229    * @return The current value of the "contentAreaFilled" property
2230    */
2231   public boolean isContentAreaFilled()
2232   {
2233     return contentAreaFilled;
2234   }
2235
2236   /**
2237    * Sets the value of the button's "contentAreaFilled" property. This
2238    * property indicates whether the area surrounding the text and icon of
2239    * the button should be filled by the look and feel class.  If this
2240    * property is <code>false</code>, the look and feel class should leave
2241    * the content area transparent.
2242    *
2243    * @param b The new value of the "contentAreaFilled" property
2244    */
2245   public void setContentAreaFilled(boolean b)
2246   {
2247     clientContentAreaFilledSet = true;
2248     if (contentAreaFilled == b)
2249       return;
2250     
2251     // The JDK sets the opaque property to the value of the contentAreaFilled
2252     // property, so should we do.
2253     setOpaque(b);
2254     boolean old = contentAreaFilled;
2255     contentAreaFilled = b;
2256     firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
2257    }
2258
2259   /**
2260    * Paints the button's border, if the button's "borderPainted" property is
2261    * <code>true</code>, by out calling to the button's look and feel class.
2262    *
2263    * @param g The graphics context used to paint the border
2264    */
2265   protected void paintBorder(Graphics g)
2266   {
2267     if (isBorderPainted())
2268       super.paintBorder(g);
2269   }
2270
2271   /**
2272    * Returns a string, used only for debugging, which identifies or somehow
2273    * represents this button. The exact value is implementation-defined.
2274    *
2275    * @return A string representation of the button
2276    */
2277   protected String paramString()
2278   {
2279     StringBuffer sb = new StringBuffer();
2280     sb.append(super.paramString());
2281     sb.append(",defaultIcon=");
2282     if (getIcon() != null)
2283       sb.append(getIcon());
2284     sb.append(",disabledIcon=");
2285     if (getDisabledIcon() != null)
2286       sb.append(getDisabledIcon());
2287     sb.append(",disabledSelectedIcon=");
2288     if (getDisabledSelectedIcon() != null)
2289       sb.append(getDisabledSelectedIcon());
2290     sb.append(",margin=");
2291     if (getMargin() != null)
2292       sb.append(getMargin());
2293     sb.append(",paintBorder=").append(isBorderPainted());
2294     sb.append(",paintFocus=").append(isFocusPainted());
2295     sb.append(",pressedIcon=");
2296     if (getPressedIcon() != null)
2297       sb.append(getPressedIcon());
2298     sb.append(",rolloverEnabled=").append(isRolloverEnabled());
2299     sb.append(",rolloverIcon=");
2300     if (getRolloverIcon() != null)
2301       sb.append(getRolloverIcon());
2302     sb.append(",rolloverSelected=");
2303     if (getRolloverSelectedIcon() != null)
2304       sb.append(getRolloverSelectedIcon());
2305     sb.append(",selectedIcon=");
2306     if (getSelectedIcon() != null)
2307       sb.append(getSelectedIcon());
2308     sb.append(",text=");
2309     if (getText() != null)
2310       sb.append(getText());
2311     return sb.toString();
2312   }
2313
2314   /**
2315    * Set the "UI" property of the button, which is a look and feel class
2316    * responsible for handling the button's input events and painting it.
2317    *
2318    * @param ui The new "UI" property
2319    */
2320   public void setUI(ButtonUI ui)
2321   {
2322     super.setUI(ui);
2323   }
2324   
2325   /**
2326    * Set the "UI" property of the button, which is a look and feel class
2327    * responsible for handling the button's input events and painting it.
2328    *
2329    * @return The current "UI" property
2330    */
2331   public ButtonUI getUI()
2332   {
2333     return (ButtonUI) ui;
2334   }
2335   
2336   /**
2337    * Set the "UI" property to a class constructed, via the {@link
2338    * UIManager}, from the current look and feel. This should be overridden
2339    * for each subclass of AbstractButton, to retrieve a suitable {@link
2340    * ButtonUI} look and feel class.
2341    */
2342   public void updateUI()
2343   {
2344     // TODO: What to do here?
2345   }
2346
2347   /**
2348    * Returns the current time in milliseconds in which clicks gets coalesced
2349    * into a single <code>ActionEvent</code>.
2350    *
2351    * @return the time in milliseconds
2352    * 
2353    * @since 1.4
2354    */
2355   public long getMultiClickThreshhold()
2356   {
2357     return multiClickThreshhold;
2358   }
2359
2360   /**
2361    * Sets the time in milliseconds in which clicks gets coalesced into a single
2362    * <code>ActionEvent</code>.
2363    *
2364    * @param threshhold the time in milliseconds
2365    * 
2366    * @since 1.4
2367    */
2368   public void setMultiClickThreshhold(long threshhold)
2369   {
2370     if (threshhold < 0)
2371       throw new IllegalArgumentException();
2372
2373     multiClickThreshhold = threshhold;
2374   }
2375
2376   /**
2377    * Adds the specified component to this AbstractButton. This overrides the
2378    * default in order to install an {@link OverlayLayout} layout manager
2379    * before adding the component. The layout manager is only installed if
2380    * no other layout manager has been installed before.
2381    *
2382    * @param comp the component to be added
2383    * @param constraints constraints for the layout manager
2384    * @param index the index at which the component is added
2385    *
2386    * @since 1.5
2387    */
2388   protected void addImpl(Component comp, Object constraints, int index)
2389   {
2390     // We use a client property here, so that no extra memory is used in
2391     // the common case with no layout manager.
2392     if (getClientProperty("AbstractButton.customLayoutSet") == null)
2393       setLayout(new OverlayLayout(this));
2394     super.addImpl(comp, constraints, index);
2395   }
2396
2397   /**
2398    * Sets a layout manager on this AbstractButton. This is overridden in order
2399    * to detect if the application sets a custom layout manager. If no custom
2400    * layout manager is set, {@link #addImpl(Component, Object, int)} installs
2401    * an OverlayLayout before adding a component.
2402    *
2403    * @param layout the layout manager to install
2404    *
2405    * @since 1.5
2406    */
2407   public void setLayout(LayoutManager layout)
2408   {
2409     // We use a client property here, so that no extra memory is used in
2410     // the common case with no layout manager.
2411     putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE);
2412     super.setLayout(layout);
2413   }
2414
2415   /**
2416    * Helper method for
2417    * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
2418    * 
2419    * @param propertyName the name of the property
2420    * @param value the value of the property
2421    *
2422    * @throws IllegalArgumentException if the specified property cannot be set
2423    *         by this method
2424    * @throws ClassCastException if the property value does not match the
2425    *         property type
2426    * @throws NullPointerException if <code>c</code> or
2427    *         <code>propertyValue</code> is <code>null</code>
2428    */
2429   void setUIProperty(String propertyName, Object value)
2430   {
2431     if (propertyName.equals("borderPainted"))
2432       {
2433         if (! clientBorderPaintedSet)
2434           {
2435             setBorderPainted(((Boolean) value).booleanValue());
2436             clientBorderPaintedSet = false;
2437           }
2438       }
2439     else if (propertyName.equals("rolloverEnabled"))
2440       {
2441         if (! clientRolloverEnabledSet)
2442           {
2443             setRolloverEnabled(((Boolean) value).booleanValue());
2444             clientRolloverEnabledSet = false;
2445           }
2446       }
2447     else if (propertyName.equals("iconTextGap"))
2448       {
2449         if (! clientIconTextGapSet)
2450           {
2451             setIconTextGap(((Integer) value).intValue());
2452             clientIconTextGapSet = false;
2453           }
2454       }
2455     else if (propertyName.equals("contentAreaFilled"))
2456       {
2457         if (! clientContentAreaFilledSet)
2458           {
2459             setContentAreaFilled(((Boolean) value).booleanValue());
2460             clientContentAreaFilledSet = false;
2461           }
2462       }
2463     else
2464       {
2465         super.setUIProperty(propertyName, value);
2466       }
2467   }
2468 }