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