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