OSDN Git Service

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