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 }