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