History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/AbstractButton.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/AbstractButton.java+3101−0
@@ -0,0 +1,3101 @@
1+/*
2+ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+package javax.swing;
26+
27+import java.awt.*;
28+import java.awt.event.*;
29+import java.awt.image.*;
30+import java.text.*;
31+import java.awt.geom.*;
32+import java.beans.PropertyChangeEvent;
33+import java.beans.PropertyChangeListener;
34+import java.beans.Transient;
35+import java.util.Enumeration;
36+import java.util.Vector;
37+import java.io.Serializable;
38+import javax.swing.event.*;
39+import javax.swing.border.*;
40+import javax.swing.plaf.*;
41+import javax.accessibility.*;
42+import javax.swing.text.*;
43+import javax.swing.text.html.*;
44+import javax.swing.plaf.basic.*;
45+import java.util.*;
46+
47+/**
48+ * Defines common behaviors for buttons and menu items.
49+ * <p>
50+ * Buttons can be configured, and to some degree controlled, by
51+ * <code><a href="Action.html">Action</a></code>s. Using an
52+ * <code>Action</code> with a button has many benefits beyond directly
53+ * configuring a button. Refer to <a href="Action.html#buttonActions">
54+ * Swing Components Supporting <code>Action</code></a> for more
55+ * details, and you can find more information in <a
56+ * href="http://java.sun.com/docs/books/tutorial/uiswing/misc/action.html">How
57+ * to Use Actions</a>, a section in <em>The Java Tutorial</em>.
58+ * <p>
59+ * For further information see
60+ * <a
61+ href="http://java.sun.com/docs/books/tutorial/uiswing/components/button.html">How to Use Buttons, Check Boxes, and Radio Buttons</a>,
62+ * a section in <em>The Java Tutorial</em>.
63+ * <p>
64+ * <strong>Warning:</strong>
65+ * Serialized objects of this class will not be compatible with
66+ * future Swing releases. The current serialization support is
67+ * appropriate for short term storage or RMI between applications running
68+ * the same version of Swing. As of 1.4, support for long term storage
69+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
70+ * has been added to the <code>java.beans</code> package.
71+ * Please see {@link java.beans.XMLEncoder}.
72+ *
73+ * @author Jeff Dinkins
74+ */
75+public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {
76+
77+ // *********************************
78+ // ******* Button properties *******
79+ // *********************************
80+
81+ /** Identifies a change in the button model. */
82+ public static final String MODEL_CHANGED_PROPERTY = "model";
83+ /** Identifies a change in the button's text. */
84+ public static final String TEXT_CHANGED_PROPERTY = "text";
85+ /** Identifies a change to the button's mnemonic. */
86+ public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
87+
88+ // Text positioning and alignment
89+ /** Identifies a change in the button's margins. */
90+ public static final String MARGIN_CHANGED_PROPERTY = "margin";
91+ /** Identifies a change in the button's vertical alignment. */
92+ public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = "verticalAlignment";
93+ /** Identifies a change in the button's horizontal alignment. */
94+ public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = "horizontalAlignment";
95+
96+ /** Identifies a change in the button's vertical text position. */
97+ public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = "verticalTextPosition";
98+ /** Identifies a change in the button's horizontal text position. */
99+ public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = "horizontalTextPosition";
100+
101+ // Paint options
102+ /**
103+ * Identifies a change to having the border drawn,
104+ * or having it not drawn.
105+ */
106+ public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
107+ /**
108+ * Identifies a change to having the border highlighted when focused,
109+ * or not.
110+ */
111+ public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
112+ /**
113+ * Identifies a change from rollover enabled to disabled or back
114+ * to enabled.
115+ */
116+ public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = "rolloverEnabled";
117+ /**
118+ * Identifies a change to having the button paint the content area.
119+ */
120+ public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = "contentAreaFilled";
121+
122+ // Icons
123+ /** Identifies a change to the icon that represents the button. */
124+ public static final String ICON_CHANGED_PROPERTY = "icon";
125+
126+ /**
127+ * Identifies a change to the icon used when the button has been
128+ * pressed.
129+ */
130+ public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
131+ /**
132+ * Identifies a change to the icon used when the button has
133+ * been selected.
134+ */
135+ public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
136+
137+ /**
138+ * Identifies a change to the icon used when the cursor is over
139+ * the button.
140+ */
141+ public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
142+ /**
143+ * Identifies a change to the icon used when the cursor is
144+ * over the button and it has been selected.
145+ */
146+ public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = "rolloverSelectedIcon";
147+
148+ /**
149+ * Identifies a change to the icon used when the button has
150+ * been disabled.
151+ */
152+ public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
153+ /**
154+ * Identifies a change to the icon used when the button has been
155+ * disabled and selected.
156+ */
157+ public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = "disabledSelectedIcon";
158+
159+
160+ /** The data model that determines the button's state. */
161+ protected ButtonModel model = null;
162+
163+ private String text = ""; // for BeanBox
164+ private Insets margin = null;
165+ private Insets defaultMargin = null;
166+
167+ // Button icons
168+ // PENDING(jeff) - hold icons in an array
169+ private Icon defaultIcon = null;
170+ private Icon pressedIcon = null;
171+ private Icon disabledIcon = null;
172+
173+ private Icon selectedIcon = null;
174+ private Icon disabledSelectedIcon = null;
175+
176+ private Icon rolloverIcon = null;
177+ private Icon rolloverSelectedIcon = null;
178+
179+ // Display properties
180+ private boolean paintBorder = true;
181+ private boolean paintFocus = true;
182+ private boolean rolloverEnabled = false;
183+ private boolean contentAreaFilled = true;
184+
185+ // Icon/Label Alignment
186+ private int verticalAlignment = CENTER;
187+ private int horizontalAlignment = CENTER;
188+
189+ private int verticalTextPosition = CENTER;
190+ private int horizontalTextPosition = TRAILING;
191+
192+ private int iconTextGap = 4;
193+
194+ private int mnemonic;
195+ private int mnemonicIndex = -1;
196+
197+ private long multiClickThreshhold = 0;
198+
199+ private boolean borderPaintedSet = false;
200+ private boolean rolloverEnabledSet = false;
201+ private boolean iconTextGapSet = false;
202+ private boolean contentAreaFilledSet = false;
203+
204+ // Whether or not we've set the LayoutManager.
205+ private boolean setLayout = false;
206+
207+ // This is only used by JButton, promoted to avoid an extra
208+ // boolean field in JButton
209+ boolean defaultCapable = true;
210+
211+ /**
212+ * Combined listeners: ActionListener, ChangeListener, ItemListener.
213+ */
214+ private Handler handler;
215+
216+ /**
217+ * The button model's <code>changeListener</code>.
218+ */
219+ protected ChangeListener changeListener = null;
220+ /**
221+ * The button model's <code>ActionListener</code>.
222+ */
223+ protected ActionListener actionListener = null;
224+ /**
225+ * The button model's <code>ItemListener</code>.
226+ */
227+ protected ItemListener itemListener = null;
228+
229+ /**
230+ * Only one <code>ChangeEvent</code> is needed per button
231+ * instance since the
232+ * event's only state is the source property. The source of events
233+ * generated is always "this".
234+ */
235+ protected transient ChangeEvent changeEvent;
236+
237+ private boolean hideActionText = false;
238+
239+ /**
240+ * Sets the <code>hideActionText</code> property, which determines
241+ * whether the button displays text from the <code>Action</code>.
242+ * This is useful only if an <code>Action</code> has been
243+ * installed on the button.
244+ *
245+ * @param hideActionText <code>true</code> if the button's
246+ * <code>text</code> property should not reflect
247+ * that of the <code>Action</code>; the default is
248+ * <code>false</code>
249+ * @see <a href="Action.html#buttonActions">Swing Components Supporting
250+ * <code>Action</code></a>
251+ * @since 1.6
252+ * @beaninfo
253+ * bound: true
254+ * expert: true
255+ * description: Whether the text of the button should come from
256+ * the <code>Action</code>.
257+ */
258+ public void setHideActionText(boolean hideActionText) {
259+ if (hideActionText != this.hideActionText) {
260+ this.hideActionText = hideActionText;
261+ if (getAction() != null) {
262+ setTextFromAction(getAction(), false);
263+ }
264+ firePropertyChange("hideActionText", !hideActionText,
265+ hideActionText);
266+ }
267+ }
268+
269+ /**
270+ * Returns the value of the <code>hideActionText</code> property, which
271+ * determines whether the button displays text from the
272+ * <code>Action</code>. This is useful only if an <code>Action</code>
273+ * has been installed on the button.
274+ *
275+ * @return <code>true</code> if the button's <code>text</code>
276+ * property should not reflect that of the
277+ * <code>Action</code>; the default is <code>false</code>
278+ * @since 1.6
279+ */
280+ public boolean getHideActionText() {
281+ return hideActionText;
282+ }
283+
284+ /**
285+ * Returns the button's text.
286+ * @return the buttons text
287+ * @see #setText
288+ */
289+ public String getText() {
290+ return text;
291+ }
292+
293+ /**
294+ * Sets the button's text.
295+ * @param text the string used to set the text
296+ * @see #getText
297+ * @beaninfo
298+ * bound: true
299+ * preferred: true
300+ * attribute: visualUpdate true
301+ * description: The button's text.
302+ */
303+ public void setText(String text) {
304+ String oldValue = this.text;
305+ this.text = text;
306+ firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);
307+ updateDisplayedMnemonicIndex(text, getMnemonic());
308+
309+ if (accessibleContext != null) {
310+ accessibleContext.firePropertyChange(
311+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
312+ oldValue, text);
313+ }
314+ if (text == null || oldValue == null || !text.equals(oldValue)) {
315+ revalidate();
316+ repaint();
317+ }
318+ }
319+
320+
321+ /**
322+ * Returns the state of the button. True if the
323+ * toggle button is selected, false if it's not.
324+ * @return true if the toggle button is selected, otherwise false
325+ */
326+ public boolean isSelected() {
327+ return model.isSelected();
328+ }
329+
330+ /**
331+ * Sets the state of the button. Note that this method does not
332+ * trigger an <code>actionEvent</code>.
333+ * Call <code>doClick</code> to perform a programatic action change.
334+ *
335+ * @param b true if the button is selected, otherwise false
336+ */
337+ public void setSelected(boolean b) {
338+ boolean oldValue = isSelected();
339+
340+ // TIGER - 4840653
341+ // Removed code which fired an AccessibleState.SELECTED
342+ // PropertyChangeEvent since this resulted in two
343+ // identical events being fired since
344+ // AbstractButton.fireItemStateChanged also fires the
345+ // same event. This caused screen readers to speak the
346+ // name of the item twice.
347+
348+ model.setSelected(b);
349+ }
350+
351+ /**
352+ * Programmatically perform a "click". This does the same
353+ * thing as if the user had pressed and released the button.
354+ */
355+ public void doClick() {
356+ doClick(68);
357+ }
358+
359+ /**
360+ * Programmatically perform a "click". This does the same
361+ * thing as if the user had pressed and released the button.
362+ * The button stays visually "pressed" for <code>pressTime</code>
363+ * milliseconds.
364+ *
365+ * @param pressTime the time to "hold down" the button, in milliseconds
366+ */
367+ public void doClick(int pressTime) {
368+ Dimension size = getSize();
369+ model.setArmed(true);
370+ model.setPressed(true);
371+ paintImmediately(new Rectangle(0,0, size.width, size.height));
372+ try {
373+ Thread.currentThread().sleep(pressTime);
374+ } catch(InterruptedException ie) {
375+ }
376+ model.setPressed(false);
377+ model.setArmed(false);
378+ }
379+
380+ /**
381+ * Sets space for margin between the button's border and
382+ * the label. Setting to <code>null</code> will cause the button to
383+ * use the default margin. The button's default <code>Border</code>
384+ * object will use this value to create the proper margin.
385+ * However, if a non-default border is set on the button,
386+ * it is that <code>Border</code> object's responsibility to create the
387+ * appropriate margin space (else this property will
388+ * effectively be ignored).
389+ *
390+ * @param m the space between the border and the label
391+ *
392+ * @beaninfo
393+ * bound: true
394+ * attribute: visualUpdate true
395+ * description: The space between the button's border and the label.
396+ */
397+ public void setMargin(Insets m) {
398+ // Cache the old margin if it comes from the UI
399+ if(m instanceof UIResource) {
400+ defaultMargin = m;
401+ } else if(margin instanceof UIResource) {
402+ defaultMargin = margin;
403+ }
404+
405+ // If the client passes in a null insets, restore the margin
406+ // from the UI if possible
407+ if(m == null && defaultMargin != null) {
408+ m = defaultMargin;
409+ }
410+
411+ Insets old = margin;
412+ margin = m;
413+ firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
414+ if (old == null || !old.equals(m)) {
415+ revalidate();
416+ repaint();
417+ }
418+ }
419+
420+ /**
421+ * Returns the margin between the button's border and
422+ * the label.
423+ *
424+ * @return an <code>Insets</code> object specifying the margin
425+ * between the botton's border and the label
426+ * @see #setMargin
427+ */
428+ public Insets getMargin() {
429+ return (margin == null) ? null : (Insets) margin.clone();
430+ }
431+
432+ /**
433+ * Returns the default icon.
434+ * @return the default <code>Icon</code>
435+ * @see #setIcon
436+ */
437+ public Icon getIcon() {
438+ return defaultIcon;
439+ }
440+
441+ /**
442+ * Sets the button's default icon. This icon is
443+ * also used as the "pressed" and "disabled" icon if
444+ * there is no explicitly set pressed icon.
445+ *
446+ * @param defaultIcon the icon used as the default image
447+ * @see #getIcon
448+ * @see #setPressedIcon
449+ * @beaninfo
450+ * bound: true
451+ * attribute: visualUpdate true
452+ * description: The button's default icon
453+ */
454+ public void setIcon(Icon defaultIcon) {
455+ Icon oldValue = this.defaultIcon;
456+ this.defaultIcon = defaultIcon;
457+
458+ /* If the default icon has really changed and we had
459+ * generated the disabled icon for this component,
460+ * (i.e. setDisabledIcon() was never called) then
461+ * clear the disabledIcon field.
462+ */
463+ if (defaultIcon != oldValue && (disabledIcon instanceof UIResource)) {
464+ disabledIcon = null;
465+ }
466+
467+ firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
468+ if (accessibleContext != null) {
469+ accessibleContext.firePropertyChange(
470+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
471+ oldValue, defaultIcon);
472+ }
473+ if (defaultIcon != oldValue) {
474+ if (defaultIcon == null || oldValue == null ||
475+ defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
476+ defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
477+ revalidate();
478+ }
479+ repaint();
480+ }
481+ }
482+
483+ /**
484+ * Returns the pressed icon for the button.
485+ * @return the <code>pressedIcon</code> property
486+ * @see #setPressedIcon
487+ */
488+ public Icon getPressedIcon() {
489+ return pressedIcon;
490+ }
491+
492+ /**
493+ * Sets the pressed icon for the button.
494+ * @param pressedIcon the icon used as the "pressed" image
495+ * @see #getPressedIcon
496+ * @beaninfo
497+ * bound: true
498+ * attribute: visualUpdate true
499+ * description: The pressed icon for the button.
500+ */
501+ public void setPressedIcon(Icon pressedIcon) {
502+ Icon oldValue = this.pressedIcon;
503+ this.pressedIcon = pressedIcon;
504+ firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
505+ if (accessibleContext != null) {
506+ accessibleContext.firePropertyChange(
507+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
508+ oldValue, pressedIcon);
509+ }
510+ if (pressedIcon != oldValue) {
511+ if (getModel().isPressed()) {
512+ repaint();
513+ }
514+ }
515+ }
516+
517+ /**
518+ * Returns the selected icon for the button.
519+ * @return the <code>selectedIcon</code> property
520+ * @see #setSelectedIcon
521+ */
522+ public Icon getSelectedIcon() {
523+ return selectedIcon;
524+ }
525+
526+ /**
527+ * Sets the selected icon for the button.
528+ * @param selectedIcon the icon used as the "selected" image
529+ * @see #getSelectedIcon
530+ * @beaninfo
531+ * bound: true
532+ * attribute: visualUpdate true
533+ * description: The selected icon for the button.
534+ */
535+ public void setSelectedIcon(Icon selectedIcon) {
536+ Icon oldValue = this.selectedIcon;
537+ this.selectedIcon = selectedIcon;
538+
539+ /* If the default selected icon has really changed and we had
540+ * generated the disabled selected icon for this component,
541+ * (i.e. setDisabledSelectedIcon() was never called) then
542+ * clear the disabledSelectedIcon field.
543+ */
544+ if (selectedIcon != oldValue &&
545+ disabledSelectedIcon instanceof UIResource) {
546+
547+ disabledSelectedIcon = null;
548+ }
549+
550+ firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
551+ if (accessibleContext != null) {
552+ accessibleContext.firePropertyChange(
553+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
554+ oldValue, selectedIcon);
555+ }
556+ if (selectedIcon != oldValue) {
557+ if (isSelected()) {
558+ repaint();
559+ }
560+ }
561+ }
562+
563+ /**
564+ * Returns the rollover icon for the button.
565+ * @return the <code>rolloverIcon</code> property
566+ * @see #setRolloverIcon
567+ */
568+ public Icon getRolloverIcon() {
569+ return rolloverIcon;
570+ }
571+
572+ /**
573+ * Sets the rollover icon for the button.
574+ * @param rolloverIcon the icon used as the "rollover" image
575+ * @see #getRolloverIcon
576+ * @beaninfo
577+ * bound: true
578+ * attribute: visualUpdate true
579+ * description: The rollover icon for the button.
580+ */
581+ public void setRolloverIcon(Icon rolloverIcon) {
582+ Icon oldValue = this.rolloverIcon;
583+ this.rolloverIcon = rolloverIcon;
584+ firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
585+ if (accessibleContext != null) {
586+ accessibleContext.firePropertyChange(
587+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
588+ oldValue, rolloverIcon);
589+ }
590+ setRolloverEnabled(true);
591+ if (rolloverIcon != oldValue) {
592+ // No way to determine whether we are currently in
593+ // a rollover state, so repaint regardless
594+ repaint();
595+ }
596+
597+ }
598+
599+ /**
600+ * Returns the rollover selection icon for the button.
601+ * @return the <code>rolloverSelectedIcon</code> property
602+ * @see #setRolloverSelectedIcon
603+ */
604+ public Icon getRolloverSelectedIcon() {
605+ return rolloverSelectedIcon;
606+ }
607+
608+ /**
609+ * Sets the rollover selected icon for the button.
610+ * @param rolloverSelectedIcon the icon used as the
611+ * "selected rollover" image
612+ * @see #getRolloverSelectedIcon
613+ * @beaninfo
614+ * bound: true
615+ * attribute: visualUpdate true
616+ * description: The rollover selected icon for the button.
617+ */
618+ public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
619+ Icon oldValue = this.rolloverSelectedIcon;
620+ this.rolloverSelectedIcon = rolloverSelectedIcon;
621+ firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
622+ if (accessibleContext != null) {
623+ accessibleContext.firePropertyChange(
624+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
625+ oldValue, rolloverSelectedIcon);
626+ }
627+ setRolloverEnabled(true);
628+ if (rolloverSelectedIcon != oldValue) {
629+ // No way to determine whether we are currently in
630+ // a rollover state, so repaint regardless
631+ if (isSelected()) {
632+ repaint();
633+ }
634+ }
635+ }
636+
637+ /**
638+ * Returns the icon used by the button when it's disabled.
639+ * If no disabled icon has been set this will forward the call to
640+ * the look and feel to construct an appropriate disabled Icon.
641+ * <p>
642+ * Some look and feels might not render the disabled Icon, in which
643+ * case they will ignore this.
644+ *
645+ * @return the <code>disabledIcon</code> property
646+ * @see #getPressedIcon
647+ * @see #setDisabledIcon
648+ * @see javax.swing.LookAndFeel#getDisabledIcon
649+ */
650+ @Transient
651+ public Icon getDisabledIcon() {
652+ if (disabledIcon == null) {
653+ disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, getIcon());
654+ if (disabledIcon != null) {
655+ firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, null, disabledIcon);
656+ }
657+ }
658+ return disabledIcon;
659+ }
660+
661+ /**
662+ * Sets the disabled icon for the button.
663+ * @param disabledIcon the icon used as the disabled image
664+ * @see #getDisabledIcon
665+ * @beaninfo
666+ * bound: true
667+ * attribute: visualUpdate true
668+ * description: The disabled icon for the button.
669+ */
670+ public void setDisabledIcon(Icon disabledIcon) {
671+ Icon oldValue = this.disabledIcon;
672+ this.disabledIcon = disabledIcon;
673+ firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
674+ if (accessibleContext != null) {
675+ accessibleContext.firePropertyChange(
676+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
677+ oldValue, disabledIcon);
678+ }
679+ if (disabledIcon != oldValue) {
680+ if (!isEnabled()) {
681+ repaint();
682+ }
683+ }
684+ }
685+
686+ /**
687+ * Returns the icon used by the button when it's disabled and selected.
688+ * If no disabled selection icon has been set, this will forward
689+ * the call to the LookAndFeel to construct an appropriate disabled
690+ * Icon from the selection icon if it has been set and to
691+ * <code>getDisabledIcon()</code> otherwise.
692+ * <p>
693+ * Some look and feels might not render the disabled selected Icon, in
694+ * which case they will ignore this.
695+ *
696+ * @return the <code>disabledSelectedIcon</code> property
697+ * @see #getDisabledIcon
698+ * @see #setDisabledSelectedIcon
699+ * @see javax.swing.LookAndFeel#getDisabledSelectedIcon
700+ */
701+ public Icon getDisabledSelectedIcon() {
702+ if (disabledSelectedIcon == null) {
703+ if (selectedIcon != null) {
704+ disabledSelectedIcon = UIManager.getLookAndFeel().
705+ getDisabledSelectedIcon(this, getSelectedIcon());
706+ } else {
707+ return getDisabledIcon();
708+ }
709+ }
710+ return disabledSelectedIcon;
711+ }
712+
713+ /**
714+ * Sets the disabled selection icon for the button.
715+ * @param disabledSelectedIcon the icon used as the disabled
716+ * selection image
717+ * @see #getDisabledSelectedIcon
718+ * @beaninfo
719+ * bound: true
720+ * attribute: visualUpdate true
721+ * description: The disabled selection icon for the button.
722+ */
723+ public void setDisabledSelectedIcon(Icon disabledSelectedIcon) {
724+ Icon oldValue = this.disabledSelectedIcon;
725+ this.disabledSelectedIcon = disabledSelectedIcon;
726+ firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, oldValue, disabledSelectedIcon);
727+ if (accessibleContext != null) {
728+ accessibleContext.firePropertyChange(
729+ AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
730+ oldValue, disabledSelectedIcon);
731+ }
732+ if (disabledSelectedIcon != oldValue) {
733+ if (disabledSelectedIcon == null || oldValue == null ||
734+ disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
735+ disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
736+ revalidate();
737+ }
738+ if (!isEnabled() && isSelected()) {
739+ repaint();
740+ }
741+ }
742+ }
743+
744+ /**
745+ * Returns the vertical alignment of the text and icon.
746+ *
747+ * @return the <code>verticalAlignment</code> property, one of the
748+ * following values:
749+ * <ul>
750+ * <li>{@code SwingConstants.CENTER} (the default)
751+ * <li>{@code SwingConstants.TOP}
752+ * <li>{@code SwingConstants.BOTTOM}
753+ * </ul>
754+ */
755+ public int getVerticalAlignment() {
756+ return verticalAlignment;
757+ }
758+
759+ /**
760+ * Sets the vertical alignment of the icon and text.
761+ * @param alignment one of the following values:
762+ * <ul>
763+ * <li>{@code SwingConstants.CENTER} (the default)
764+ * <li>{@code SwingConstants.TOP}
765+ * <li>{@code SwingConstants.BOTTOM}
766+ * </ul>
767+ * @throws IllegalArgumentException if the alignment is not one of the legal
768+ * values listed above
769+ * @beaninfo
770+ * bound: true
771+ * enum: TOP SwingConstants.TOP
772+ * CENTER SwingConstants.CENTER
773+ * BOTTOM SwingConstants.BOTTOM
774+ * attribute: visualUpdate true
775+ * description: The vertical alignment of the icon and text.
776+ */
777+ public void setVerticalAlignment(int alignment) {
778+ if (alignment == verticalAlignment) return;
779+ int oldValue = verticalAlignment;
780+ verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
781+ firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment); repaint();
782+ }
783+
784+ /**
785+ * Returns the horizontal alignment of the icon and text.
786+ * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
787+ * but subclasses such as {@code JCheckBox} may use a different default.
788+ *
789+ * @return the <code>horizontalAlignment</code> property,
790+ * one of the following values:
791+ * <ul>
792+ * <li>{@code SwingConstants.RIGHT}
793+ * <li>{@code SwingConstants.LEFT}
794+ * <li>{@code SwingConstants.CENTER}
795+ * <li>{@code SwingConstants.LEADING}
796+ * <li>{@code SwingConstants.TRAILING}
797+ * </ul>
798+ */
799+ public int getHorizontalAlignment() {
800+ return horizontalAlignment;
801+ }
802+
803+ /**
804+ * Sets the horizontal alignment of the icon and text.
805+ * {@code AbstractButton}'s default is {@code SwingConstants.CENTER},
806+ * but subclasses such as {@code JCheckBox} may use a different default.
807+ *
808+ * @param alignment the alignment value, one of the following values:
809+ * <ul>
810+ * <li>{@code SwingConstants.RIGHT}
811+ * <li>{@code SwingConstants.LEFT}
812+ * <li>{@code SwingConstants.CENTER}
813+ * <li>{@code SwingConstants.LEADING}
814+ * <li>{@code SwingConstants.TRAILING}
815+ * </ul>
816+ * @throws IllegalArgumentException if the alignment is not one of the
817+ * valid values
818+ * @beaninfo
819+ * bound: true
820+ * enum: LEFT SwingConstants.LEFT
821+ * CENTER SwingConstants.CENTER
822+ * RIGHT SwingConstants.RIGHT
823+ * LEADING SwingConstants.LEADING
824+ * TRAILING SwingConstants.TRAILING
825+ * attribute: visualUpdate true
826+ * description: The horizontal alignment of the icon and text.
827+ */
828+ public void setHorizontalAlignment(int alignment) {
829+ if (alignment == horizontalAlignment) return;
830+ int oldValue = horizontalAlignment;
831+ horizontalAlignment = checkHorizontalKey(alignment,
832+ "horizontalAlignment");
833+ firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
834+ oldValue, horizontalAlignment);
835+ repaint();
836+ }
837+
838+
839+ /**
840+ * Returns the vertical position of the text relative to the icon.
841+ * @return the <code>verticalTextPosition</code> property,
842+ * one of the following values:
843+ * <ul>
844+ * <li>{@code SwingConstants.CENTER} (the default)
845+ * <li>{@code SwingConstants.TOP}
846+ * <li>{@code SwingConstants.BOTTOM}
847+ * </ul>
848+ */
849+ public int getVerticalTextPosition() {
850+ return verticalTextPosition;
851+ }
852+
853+ /**
854+ * Sets the vertical position of the text relative to the icon.
855+ * @param textPosition one of the following values:
856+ * <ul>
857+ * <li>{@code SwingConstants.CENTER} (the default)
858+ * <li>{@code SwingConstants.TOP}
859+ * <li>{@code SwingConstants.BOTTOM}
860+ * </ul>
861+ * @beaninfo
862+ * bound: true
863+ * enum: TOP SwingConstants.TOP
864+ * CENTER SwingConstants.CENTER
865+ * BOTTOM SwingConstants.BOTTOM
866+ * attribute: visualUpdate true
867+ * description: The vertical position of the text relative to the icon.
868+ */
869+ public void setVerticalTextPosition(int textPosition) {
870+ if (textPosition == verticalTextPosition) return;
871+ int oldValue = verticalTextPosition;
872+ verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
873+ firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
874+ revalidate();
875+ repaint();
876+ }
877+
878+ /**
879+ * Returns the horizontal position of the text relative to the icon.
880+ * @return the <code>horizontalTextPosition</code> property,
881+ * one of the following values:
882+ * <ul>
883+ * <li>{@code SwingConstants.RIGHT}
884+ * <li>{@code SwingConstants.LEFT}
885+ * <li>{@code SwingConstants.CENTER}
886+ * <li>{@code SwingConstants.LEADING}
887+ * <li>{@code SwingConstants.TRAILING} (the default)
888+ * </ul>
889+ */
890+ public int getHorizontalTextPosition() {
891+ return horizontalTextPosition;
892+ }
893+
894+ /**
895+ * Sets the horizontal position of the text relative to the icon.
896+ * @param textPosition one of the following values:
897+ * <ul>
898+ * <li>{@code SwingConstants.RIGHT}
899+ * <li>{@code SwingConstants.LEFT}
900+ * <li>{@code SwingConstants.CENTER}
901+ * <li>{@code SwingConstants.LEADING}
902+ * <li>{@code SwingConstants.TRAILING} (the default)
903+ * </ul>
904+ * @exception IllegalArgumentException if <code>textPosition</code>
905+ * is not one of the legal values listed above
906+ * @beaninfo
907+ * bound: true
908+ * enum: LEFT SwingConstants.LEFT
909+ * CENTER SwingConstants.CENTER
910+ * RIGHT SwingConstants.RIGHT
911+ * LEADING SwingConstants.LEADING
912+ * TRAILING SwingConstants.TRAILING
913+ * attribute: visualUpdate true
914+ * description: The horizontal position of the text relative to the icon.
915+ */
916+ public void setHorizontalTextPosition(int textPosition) {
917+ if (textPosition == horizontalTextPosition) return;
918+ int oldValue = horizontalTextPosition;
919+ horizontalTextPosition = checkHorizontalKey(textPosition,
920+ "horizontalTextPosition");
921+ firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
922+ oldValue,
923+ horizontalTextPosition);
924+ revalidate();
925+ repaint();
926+ }
927+
928+ /**
929+ * Returns the amount of space between the text and the icon
930+ * displayed in this button.
931+ *
932+ * @return an int equal to the number of pixels between the text
933+ * and the icon.
934+ * @since 1.4
935+ * @see #setIconTextGap
936+ */
937+ public int getIconTextGap() {
938+ return iconTextGap;
939+ }
940+
941+ /**
942+ * If both the icon and text properties are set, this property
943+ * defines the space between them.
944+ * <p>
945+ * The default value of this property is 4 pixels.
946+ * <p>
947+ * This is a JavaBeans bound property.
948+ *
949+ * @since 1.4
950+ * @see #getIconTextGap
951+ * @beaninfo
952+ * bound: true
953+ * attribute: visualUpdate true
954+ * description: If both the icon and text properties are set, this
955+ * property defines the space between them.
956+ */
957+ public void setIconTextGap(int iconTextGap) {
958+ int oldValue = this.iconTextGap;
959+ this.iconTextGap = iconTextGap;
960+ iconTextGapSet = true;
961+ firePropertyChange("iconTextGap", oldValue, iconTextGap);
962+ if (iconTextGap != oldValue) {
963+ revalidate();
964+ repaint();
965+ }
966+ }
967+
968+ /**
969+ * Verify that the {@code key} argument is a legal value for the
970+ * {@code horizontalAlignment} and {@code horizontalTextPosition}
971+ * properties. Valid values are:
972+ * <ul>
973+ * <li>{@code SwingConstants.RIGHT}
974+ * <li>{@code SwingConstants.LEFT}
975+ * <li>{@code SwingConstants.CENTER}
976+ * <li>{@code SwingConstants.LEADING}
977+ * <li>{@code SwingConstants.TRAILING}
978+ * </ul>
979+ *
980+ * @param key the property value to check
981+ * @param exception the message to use in the
982+ * {@code IllegalArgumentException} that is thrown for an invalid
983+ * value
984+ * @exception IllegalArgumentException if key is not one of the legal
985+ * values listed above
986+ * @see #setHorizontalTextPosition
987+ * @see #setHorizontalAlignment
988+ */
989+ protected int checkHorizontalKey(int key, String exception) {
990+ if ((key == LEFT) ||
991+ (key == CENTER) ||
992+ (key == RIGHT) ||
993+ (key == LEADING) ||
994+ (key == TRAILING)) {
995+ return key;
996+ } else {
997+ throw new IllegalArgumentException(exception);
998+ }
999+ }
1000+
1001+ /**
1002+ * Verify that the {@code key} argument is a legal value for the
1003+ * vertical properties. Valid values are:
1004+ * <ul>
1005+ * <li>{@code SwingConstants.CENTER}
1006+ * <li>{@code SwingConstants.TOP}
1007+ * <li>{@code SwingConstants.BOTTOM}
1008+ * </ul>
1009+ *
1010+ * @param key the property value to check
1011+ * @param exception the message to use in the
1012+ * {@code IllegalArgumentException} that is thrown for an invalid
1013+ * value
1014+ * @exception IllegalArgumentException if key is not one of the legal
1015+ * values listed above
1016+ */
1017+ protected int checkVerticalKey(int key, String exception) {
1018+ if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {
1019+ return key;
1020+ } else {
1021+ throw new IllegalArgumentException(exception);
1022+ }
1023+ }
1024+
1025+ /**
1026+ *{@inheritDoc}
1027+ *
1028+ * @since 1.6
1029+ */
1030+ public void removeNotify() {
1031+ super.removeNotify();
1032+ if(isRolloverEnabled()) {
1033+ getModel().setRollover(false);
1034+ }
1035+ }
1036+
1037+ /**
1038+ * Sets the action command for this button.
1039+ * @param actionCommand the action command for this button
1040+ */
1041+ public void setActionCommand(String actionCommand) {
1042+ getModel().setActionCommand(actionCommand);
1043+ }
1044+
1045+ /**
1046+ * Returns the action command for this button.
1047+ * @return the action command for this button
1048+ */
1049+ public String getActionCommand() {
1050+ String ac = getModel().getActionCommand();
1051+ if(ac == null) {
1052+ ac = getText();
1053+ }
1054+ return ac;
1055+ }
1056+
1057+ private Action action;
1058+ private PropertyChangeListener actionPropertyChangeListener;
1059+
1060+ /**
1061+ * Sets the <code>Action</code>.
1062+ * The new <code>Action</code> replaces any previously set
1063+ * <code>Action</code> but does not affect <code>ActionListeners</code>
1064+ * independently added with <code>addActionListener</code>.
1065+ * If the <code>Action</code> is already a registered
1066+ * <code>ActionListener</code> for the button, it is not re-registered.
1067+ * <p>
1068+ * Setting the <code>Action</code> results in immediately changing
1069+ * all the properties described in <a href="Action.html#buttonActions">
1070+ * Swing Components Supporting <code>Action</code></a>.
1071+ * Subsequently, the button's properties are automatically updated
1072+ * as the <code>Action</code>'s properties change.
1073+ * <p>
1074+ * This method uses three other methods to set
1075+ * and help track the <code>Action</code>'s property values.
1076+ * It uses the <code>configurePropertiesFromAction</code> method
1077+ * to immediately change the button's properties.
1078+ * To track changes in the <code>Action</code>'s property values,
1079+ * this method registers the <code>PropertyChangeListener</code>
1080+ * returned by <code>createActionPropertyChangeListener</code>. The
1081+ * default {@code PropertyChangeListener} invokes the
1082+ * {@code actionPropertyChanged} method when a property in the
1083+ * {@code Action} changes.
1084+ *
1085+ * @param a the <code>Action</code> for the <code>AbstractButton</code>,
1086+ * or <code>null</code>
1087+ * @since 1.3
1088+ * @see Action
1089+ * @see #getAction
1090+ * @see #configurePropertiesFromAction
1091+ * @see #createActionPropertyChangeListener
1092+ * @see #actionPropertyChanged
1093+ * @beaninfo
1094+ * bound: true
1095+ * attribute: visualUpdate true
1096+ * description: the Action instance connected with this ActionEvent source
1097+ */
1098+ public void setAction(Action a) {
1099+ Action oldValue = getAction();
1100+ if (action==null || !action.equals(a)) {
1101+ action = a;
1102+ if (oldValue!=null) {
1103+ removeActionListener(oldValue);
1104+ oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1105+ actionPropertyChangeListener = null;
1106+ }
1107+ configurePropertiesFromAction(action);
1108+ if (action!=null) {
1109+ // Don't add if it is already a listener
1110+ if (!isListener(ActionListener.class, action)) {
1111+ addActionListener(action);
1112+ }
1113+ // Reverse linkage:
1114+ actionPropertyChangeListener = createActionPropertyChangeListener(action);
1115+ action.addPropertyChangeListener(actionPropertyChangeListener);
1116+ }
1117+ firePropertyChange("action", oldValue, action);
1118+ }
1119+ }
1120+
1121+ private boolean isListener(Class c, ActionListener a) {
1122+ boolean isListener = false;
1123+ Object[] listeners = listenerList.getListenerList();
1124+ for (int i = listeners.length-2; i>=0; i-=2) {
1125+ if (listeners[i]==c && listeners[i+1]==a) {
1126+ isListener=true;
1127+ }
1128+ }
1129+ return isListener;
1130+ }
1131+
1132+ /**
1133+ * Returns the currently set <code>Action</code> for this
1134+ * <code>ActionEvent</code> source, or <code>null</code>
1135+ * if no <code>Action</code> is set.
1136+ *
1137+ * @return the <code>Action</code> for this <code>ActionEvent</code>
1138+ * source, or <code>null</code>
1139+ * @since 1.3
1140+ * @see Action
1141+ * @see #setAction
1142+ */
1143+ public Action getAction() {
1144+ return action;
1145+ }
1146+
1147+ /**
1148+ * Sets the properties on this button to match those in the specified
1149+ * <code>Action</code>. Refer to <a href="Action.html#buttonActions">
1150+ * Swing Components Supporting <code>Action</code></a> for more
1151+ * details as to which properties this sets.
1152+ *
1153+ * @param a the <code>Action</code> from which to get the properties,
1154+ * or <code>null</code>
1155+ * @since 1.3
1156+ * @see Action
1157+ * @see #setAction
1158+ */
1159+ protected void configurePropertiesFromAction(Action a) {
1160+ setMnemonicFromAction(a);
1161+ setTextFromAction(a, false);
1162+ AbstractAction.setToolTipTextFromAction(this, a);
1163+ setIconFromAction(a);
1164+ setActionCommandFromAction(a);
1165+ AbstractAction.setEnabledFromAction(this, a);
1166+ if (AbstractAction.hasSelectedKey(a) &&
1167+ shouldUpdateSelectedStateFromAction()) {
1168+ setSelectedFromAction(a);
1169+ }
1170+ setDisplayedMnemonicIndexFromAction(a, false);
1171+ }
1172+
1173+ void clientPropertyChanged(Object key, Object oldValue,
1174+ Object newValue) {
1175+ if (key == "hideActionText") {
1176+ boolean current = (newValue instanceof Boolean) ?
1177+ (Boolean)newValue : false;
1178+ if (getHideActionText() != current) {
1179+ setHideActionText(current);
1180+ }
1181+ }
1182+ }
1183+
1184+ /**
1185+ * Button subclasses that support mirroring the selected state from
1186+ * the action should override this to return true. AbstractButton's
1187+ * implementation returns false.
1188+ */
1189+ boolean shouldUpdateSelectedStateFromAction() {
1190+ return false;
1191+ }
1192+
1193+ /**
1194+ * Updates the button's state in response to property changes in the
1195+ * associated action. This method is invoked from the
1196+ * {@code PropertyChangeListener} returned from
1197+ * {@code createActionPropertyChangeListener}. Subclasses do not normally
1198+ * need to invoke this. Subclasses that support additional {@code Action}
1199+ * properties should override this and
1200+ * {@code configurePropertiesFromAction}.
1201+ * <p>
1202+ * Refer to the table at <a href="Action.html#buttonActions">
1203+ * Swing Components Supporting <code>Action</code></a> for a list of
1204+ * the properties this method sets.
1205+ *
1206+ * @param action the <code>Action</code> associated with this button
1207+ * @param propertyName the name of the property that changed
1208+ * @since 1.6
1209+ * @see Action
1210+ * @see #configurePropertiesFromAction
1211+ */
1212+ protected void actionPropertyChanged(Action action, String propertyName) {
1213+ if (propertyName == Action.NAME) {
1214+ setTextFromAction(action, true);
1215+ } else if (propertyName == "enabled") {
1216+ AbstractAction.setEnabledFromAction(this, action);
1217+ } else if (propertyName == Action.SHORT_DESCRIPTION) {
1218+ AbstractAction.setToolTipTextFromAction(this, action);
1219+ } else if (propertyName == Action.SMALL_ICON) {
1220+ smallIconChanged(action);
1221+ } else if (propertyName == Action.MNEMONIC_KEY) {
1222+ setMnemonicFromAction(action);
1223+ } else if (propertyName == Action.ACTION_COMMAND_KEY) {
1224+ setActionCommandFromAction(action);
1225+ } else if (propertyName == Action.SELECTED_KEY &&
1226+ AbstractAction.hasSelectedKey(action) &&
1227+ shouldUpdateSelectedStateFromAction()) {
1228+ setSelectedFromAction(action);
1229+ } else if (propertyName == Action.DISPLAYED_MNEMONIC_INDEX_KEY) {
1230+ setDisplayedMnemonicIndexFromAction(action, true);
1231+ } else if (propertyName == Action.LARGE_ICON_KEY) {
1232+ largeIconChanged(action);
1233+ }
1234+ }
1235+
1236+ private void setDisplayedMnemonicIndexFromAction(
1237+ Action a, boolean fromPropertyChange) {
1238+ Integer iValue = (a == null) ? null :
1239+ (Integer)a.getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY);
1240+ if (fromPropertyChange || iValue != null) {
1241+ int value;
1242+ if (iValue == null) {
1243+ value = -1;
1244+ } else {
1245+ value = iValue;
1246+ String text = getText();
1247+ if (text == null || value >= text.length()) {
1248+ value = -1;
1249+ }
1250+ }
1251+ setDisplayedMnemonicIndex(value);
1252+ }
1253+ }
1254+
1255+ private void setMnemonicFromAction(Action a) {
1256+ Integer n = (a == null) ? null :
1257+ (Integer)a.getValue(Action.MNEMONIC_KEY);
1258+ setMnemonic((n == null) ? '\0' : n);
1259+ }
1260+
1261+ private void setTextFromAction(Action a, boolean propertyChange) {
1262+ boolean hideText = getHideActionText();
1263+ if (!propertyChange) {
1264+ setText((a != null && !hideText) ?
1265+ (String)a.getValue(Action.NAME) : null);
1266+ }
1267+ else if (!hideText) {
1268+ setText((String)a.getValue(Action.NAME));
1269+ }
1270+ }
1271+
1272+ void setIconFromAction(Action a) {
1273+ Icon icon = null;
1274+ if (a != null) {
1275+ icon = (Icon)a.getValue(Action.LARGE_ICON_KEY);
1276+ if (icon == null) {
1277+ icon = (Icon)a.getValue(Action.SMALL_ICON);
1278+ }
1279+ }
1280+ setIcon(icon);
1281+ }
1282+
1283+ void smallIconChanged(Action a) {
1284+ if (a.getValue(Action.LARGE_ICON_KEY) == null) {
1285+ setIconFromAction(a);
1286+ }
1287+ }
1288+
1289+ void largeIconChanged(Action a) {
1290+ setIconFromAction(a);
1291+ }
1292+
1293+ private void setActionCommandFromAction(Action a) {
1294+ setActionCommand((a != null) ?
1295+ (String)a.getValue(Action.ACTION_COMMAND_KEY) :
1296+ null);
1297+ }
1298+
1299+ /**
1300+ * Sets the seleted state of the button from the action. This is defined
1301+ * here, but not wired up. Subclasses like JToggleButton and
1302+ * JCheckBoxMenuItem make use of it.
1303+ *
1304+ * @param a the Action
1305+ */
1306+ private void setSelectedFromAction(Action a) {
1307+ boolean selected = false;
1308+ if (a != null) {
1309+ selected = AbstractAction.isSelected(a);
1310+ }
1311+ if (selected != isSelected()) {
1312+ // This won't notify ActionListeners, but that should be
1313+ // ok as the change is coming from the Action.
1314+ setSelected(selected);
1315+ // Make sure the change actually took effect
1316+ if (!selected && isSelected()) {
1317+ if (getModel() instanceof DefaultButtonModel) {
1318+ ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup();
1319+ if (group != null) {
1320+ group.clearSelection();
1321+ }
1322+ }
1323+ }
1324+ }
1325+ }
1326+
1327+ /**
1328+ * Creates and returns a <code>PropertyChangeListener</code> that is
1329+ * responsible for listening for changes from the specified
1330+ * <code>Action</code> and updating the appropriate properties.
1331+ * <p>
1332+ * <b>Warning:</b> If you subclass this do not create an anonymous
1333+ * inner class. If you do the lifetime of the button will be tied to
1334+ * that of the <code>Action</code>.
1335+ *
1336+ * @param a the button's action
1337+ * @since 1.3
1338+ * @see Action
1339+ * @see #setAction
1340+ */
1341+ protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1342+ return createActionPropertyChangeListener0(a);
1343+ }
1344+
1345+
1346+ PropertyChangeListener createActionPropertyChangeListener0(Action a) {
1347+ return new ButtonActionPropertyChangeListener(this, a);
1348+ }
1349+
1350+ private static class ButtonActionPropertyChangeListener
1351+ extends ActionPropertyChangeListener<AbstractButton> {
1352+ ButtonActionPropertyChangeListener(AbstractButton b, Action a) {
1353+ super(b, a);
1354+ }
1355+ protected void actionPropertyChanged(AbstractButton button,
1356+ Action action,
1357+ PropertyChangeEvent e) {
1358+ if (AbstractAction.shouldReconfigure(e)) {
1359+ button.configurePropertiesFromAction(action);
1360+ } else {
1361+ button.actionPropertyChanged(action, e.getPropertyName());
1362+ }
1363+ }
1364+ }
1365+
1366+ /**
1367+ * Gets the <code>borderPainted</code> property.
1368+ *
1369+ * @return the value of the <code>borderPainted</code> property
1370+ * @see #setBorderPainted
1371+ */
1372+ public boolean isBorderPainted() {
1373+ return paintBorder;
1374+ }
1375+
1376+ /**
1377+ * Sets the <code>borderPainted</code> property.
1378+ * If <code>true</code> and the button has a border,
1379+ * the border is painted. The default value for the
1380+ * <code>borderPainted</code> property is <code>true</code>.
1381+ * <p/>
1382+ * Some look and feels might not support
1383+ * the <code>borderPainted</code> property,
1384+ * in which case they ignore this.
1385+ *
1386+ * @param b if true and border property is not <code>null</code>,
1387+ * the border is painted
1388+ * @see #isBorderPainted
1389+ * @beaninfo
1390+ * bound: true
1391+ * attribute: visualUpdate true
1392+ * description: Whether the border should be painted.
1393+ */
1394+ public void setBorderPainted(boolean b) {
1395+ boolean oldValue = paintBorder;
1396+ paintBorder = b;
1397+ borderPaintedSet = true;
1398+ firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
1399+ if (b != oldValue) {
1400+ revalidate();
1401+ repaint();
1402+ }
1403+ }
1404+
1405+ /**
1406+ * Paint the button's border if <code>BorderPainted</code>
1407+ * property is true and the button has a border.
1408+ * @param g the <code>Graphics</code> context in which to paint
1409+ *
1410+ * @see #paint
1411+ * @see #setBorder
1412+ */
1413+ protected void paintBorder(Graphics g) {
1414+ if (isBorderPainted()) {
1415+ super.paintBorder(g);
1416+ }
1417+ }
1418+
1419+ /**
1420+ * Gets the <code>paintFocus</code> property.
1421+ *
1422+ * @return the <code>paintFocus</code> property
1423+ * @see #setFocusPainted
1424+ */
1425+ public boolean isFocusPainted() {
1426+ return paintFocus;
1427+ }
1428+
1429+ /**
1430+ * Sets the <code>paintFocus</code> property, which must
1431+ * be <code>true</code> for the focus state to be painted.
1432+ * The default value for the <code>paintFocus</code> property
1433+ * is <code>true</code>.
1434+ * Some look and feels might not paint focus state;
1435+ * they will ignore this property.
1436+ *
1437+ * @param b if <code>true</code>, the focus state should be painted
1438+ * @see #isFocusPainted
1439+ * @beaninfo
1440+ * bound: true
1441+ * attribute: visualUpdate true
1442+ * description: Whether focus should be painted
1443+ */
1444+ public void setFocusPainted(boolean b) {
1445+ boolean oldValue = paintFocus;
1446+ paintFocus = b;
1447+ firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
1448+ if (b != oldValue && isFocusOwner()) {
1449+ revalidate();
1450+ repaint();
1451+ }
1452+ }
1453+
1454+ /**
1455+ * Gets the <code>contentAreaFilled</code> property.
1456+ *
1457+ * @return the <code>contentAreaFilled</code> property
1458+ * @see #setContentAreaFilled
1459+ */
1460+ public boolean isContentAreaFilled() {
1461+ return contentAreaFilled;
1462+ }
1463+
1464+ /**
1465+ * Sets the <code>contentAreaFilled</code> property.
1466+ * If <code>true</code> the button will paint the content
1467+ * area. If you wish to have a transparent button, such as
1468+ * an icon only button, for example, then you should set
1469+ * this to <code>false</code>. Do not call <code>setOpaque(false)</code>.
1470+ * The default value for the the <code>contentAreaFilled</code>
1471+ * property is <code>true</code>.
1472+ * <p>
1473+ * This function may cause the component's opaque property to change.
1474+ * <p>
1475+ * The exact behavior of calling this function varies on a
1476+ * component-by-component and L&F-by-L&F basis.
1477+ *
1478+ * @param b if true, the content should be filled; if false
1479+ * the content area is not filled
1480+ * @see #isContentAreaFilled
1481+ * @see #setOpaque
1482+ * @beaninfo
1483+ * bound: true
1484+ * attribute: visualUpdate true
1485+ * description: Whether the button should paint the content area
1486+ * or leave it transparent.
1487+ */
1488+ public void setContentAreaFilled(boolean b) {
1489+ boolean oldValue = contentAreaFilled;
1490+ contentAreaFilled = b;
1491+ contentAreaFilledSet = true;
1492+ firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
1493+ if (b != oldValue) {
1494+ repaint();
1495+ }
1496+ }
1497+
1498+ /**
1499+ * Gets the <code>rolloverEnabled</code> property.
1500+ *
1501+ * @return the value of the <code>rolloverEnabled</code> property
1502+ * @see #setRolloverEnabled
1503+ */
1504+ public boolean isRolloverEnabled() {
1505+ return rolloverEnabled;
1506+ }
1507+
1508+ /**
1509+ * Sets the <code>rolloverEnabled</code> property, which
1510+ * must be <code>true</code> for rollover effects to occur.
1511+ * The default value for the <code>rolloverEnabled</code>
1512+ * property is <code>false</code>.
1513+ * Some look and feels might not implement rollover effects;
1514+ * they will ignore this property.
1515+ *
1516+ * @param b if <code>true</code>, rollover effects should be painted
1517+ * @see #isRolloverEnabled
1518+ * @beaninfo
1519+ * bound: true
1520+ * attribute: visualUpdate true
1521+ * description: Whether rollover effects should be enabled.
1522+ */
1523+ public void setRolloverEnabled(boolean b) {
1524+ boolean oldValue = rolloverEnabled;
1525+ rolloverEnabled = b;
1526+ rolloverEnabledSet = true;
1527+ firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
1528+ if (b != oldValue) {
1529+ repaint();
1530+ }
1531+ }
1532+
1533+ /**
1534+ * Returns the keyboard mnemonic from the the current model.
1535+ * @return the keyboard mnemonic from the model
1536+ */
1537+ public int getMnemonic() {
1538+ return mnemonic;
1539+ }
1540+
1541+ /**
1542+ * Sets the keyboard mnemonic on the current model.
1543+ * The mnemonic is the key which when combined with the look and feel's
1544+ * mouseless modifier (usually Alt) will activate this button
1545+ * if focus is contained somewhere within this button's ancestor
1546+ * window.
1547+ * <p>
1548+ * A mnemonic must correspond to a single key on the keyboard
1549+ * and should be specified using one of the <code>VK_XXX</code>
1550+ * keycodes defined in <code>java.awt.event.KeyEvent</code>.
1551+ * These codes and the wider array of codes for international
1552+ * keyboards may be obtained through
1553+ * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>.
1554+ * Mnemonics are case-insensitive, therefore a key event
1555+ * with the corresponding keycode would cause the button to be
1556+ * activated whether or not the Shift modifier was pressed.
1557+ * <p>
1558+ * If the character defined by the mnemonic is found within
1559+ * the button's label string, the first occurrence of it
1560+ * will be underlined to indicate the mnemonic to the user.
1561+ *
1562+ * @param mnemonic the key code which represents the mnemonic
1563+ * @see java.awt.event.KeyEvent
1564+ * @see #setDisplayedMnemonicIndex
1565+ *
1566+ * @beaninfo
1567+ * bound: true
1568+ * attribute: visualUpdate true
1569+ * description: the keyboard character mnemonic
1570+ */
1571+ public void setMnemonic(int mnemonic) {
1572+ int oldValue = getMnemonic();
1573+ model.setMnemonic(mnemonic);
1574+ updateMnemonicProperties();
1575+ }
1576+
1577+ /**
1578+ * This method is now obsolete, please use <code>setMnemonic(int)</code>
1579+ * to set the mnemonic for a button. This method is only designed
1580+ * to handle character values which fall between 'a' and 'z' or
1581+ * 'A' and 'Z'.
1582+ *
1583+ * @param mnemonic a char specifying the mnemonic value
1584+ * @see #setMnemonic(int)
1585+ * @beaninfo
1586+ * bound: true
1587+ * attribute: visualUpdate true
1588+ * description: the keyboard character mnemonic
1589+ */
1590+ public void setMnemonic(char mnemonic) {
1591+ int vk = (int) mnemonic;
1592+ if(vk >= 'a' && vk <='z')
1593+ vk -= ('a' - 'A');
1594+ setMnemonic(vk);
1595+ }
1596+
1597+ /**
1598+ * Provides a hint to the look and feel as to which character in the
1599+ * text should be decorated to represent the mnemonic. Not all look and
1600+ * feels may support this. A value of -1 indicates either there is no
1601+ * mnemonic, the mnemonic character is not contained in the string, or
1602+ * the developer does not wish the mnemonic to be displayed.
1603+ * <p>
1604+ * The value of this is updated as the properties relating to the
1605+ * mnemonic change (such as the mnemonic itself, the text...).
1606+ * You should only ever have to call this if
1607+ * you do not wish the default character to be underlined. For example, if
1608+ * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A'
1609+ * to be decorated, as 'Save <u>A</u>s', you would have to invoke
1610+ * <code>setDisplayedMnemonicIndex(5)</code> after invoking
1611+ * <code>setMnemonic(KeyEvent.VK_A)</code>.
1612+ *
1613+ * @since 1.4
1614+ * @param index Index into the String to underline
1615+ * @exception IllegalArgumentException will be thrown if <code>index</code>
1616+ * is &gt;= length of the text, or &lt; -1
1617+ * @see #getDisplayedMnemonicIndex
1618+ *
1619+ * @beaninfo
1620+ * bound: true
1621+ * attribute: visualUpdate true
1622+ * description: the index into the String to draw the keyboard character
1623+ * mnemonic at
1624+ */
1625+ public void setDisplayedMnemonicIndex(int index)
1626+ throws IllegalArgumentException {
1627+ int oldValue = mnemonicIndex;
1628+ if (index == -1) {
1629+ mnemonicIndex = -1;
1630+ } else {
1631+ String text = getText();
1632+ int textLength = (text == null) ? 0 : text.length();
1633+ if (index < -1 || index >= textLength) { // index out of range
1634+ throw new IllegalArgumentException("index == " + index);
1635+ }
1636+ }
1637+ mnemonicIndex = index;
1638+ firePropertyChange("displayedMnemonicIndex", oldValue, index);
1639+ if (index != oldValue) {
1640+ revalidate();
1641+ repaint();
1642+ }
1643+ }
1644+
1645+ /**
1646+ * Returns the character, as an index, that the look and feel should
1647+ * provide decoration for as representing the mnemonic character.
1648+ *
1649+ * @since 1.4
1650+ * @return index representing mnemonic character
1651+ * @see #setDisplayedMnemonicIndex
1652+ */
1653+ public int getDisplayedMnemonicIndex() {
1654+ return mnemonicIndex;
1655+ }
1656+
1657+ /**
1658+ * Update the displayedMnemonicIndex property. This method
1659+ * is called when either text or mnemonic changes. The new
1660+ * value of the displayedMnemonicIndex property is the index
1661+ * of the first occurrence of mnemonic in text.
1662+ */
1663+ private void updateDisplayedMnemonicIndex(String text, int mnemonic) {
1664+ setDisplayedMnemonicIndex(
1665+ SwingUtilities.findDisplayedMnemonicIndex(text, mnemonic));
1666+ }
1667+
1668+ /**
1669+ * Brings the mnemonic property in accordance with model's mnemonic.
1670+ * This is called when model's mnemonic changes. Also updates the
1671+ * displayedMnemonicIndex property.
1672+ */
1673+ private void updateMnemonicProperties() {
1674+ int newMnemonic = model.getMnemonic();
1675+ if (mnemonic != newMnemonic) {
1676+ int oldValue = mnemonic;
1677+ mnemonic = newMnemonic;
1678+ firePropertyChange(MNEMONIC_CHANGED_PROPERTY,
1679+ oldValue, mnemonic);
1680+ updateDisplayedMnemonicIndex(getText(), mnemonic);
1681+ revalidate();
1682+ repaint();
1683+ }
1684+ }
1685+
1686+ /**
1687+ * Sets the amount of time (in milliseconds) required between
1688+ * mouse press events for the button to generate the corresponding
1689+ * action events. After the initial mouse press occurs (and action
1690+ * event generated) any subsequent mouse press events which occur
1691+ * on intervals less than the threshhold will be ignored and no
1692+ * corresponding action event generated. By default the threshhold is 0,
1693+ * which means that for each mouse press, an action event will be
1694+ * fired, no matter how quickly the mouse clicks occur. In buttons
1695+ * where this behavior is not desirable (for example, the "OK" button
1696+ * in a dialog), this threshhold should be set to an appropriate
1697+ * positive value.
1698+ *
1699+ * @see #getMultiClickThreshhold
1700+ * @param threshhold the amount of time required between mouse
1701+ * press events to generate corresponding action events
1702+ * @exception IllegalArgumentException if threshhold < 0
1703+ * @since 1.4
1704+ */
1705+ public void setMultiClickThreshhold(long threshhold) {
1706+ if (threshhold < 0) {
1707+ throw new IllegalArgumentException("threshhold must be >= 0");
1708+ }
1709+ this.multiClickThreshhold = threshhold;
1710+ }
1711+
1712+ /**
1713+ * Gets the amount of time (in milliseconds) required between
1714+ * mouse press events for the button to generate the corresponding
1715+ * action events.
1716+ *
1717+ * @see #setMultiClickThreshhold
1718+ * @return the amount of time required between mouse press events
1719+ * to generate corresponding action events
1720+ * @since 1.4
1721+ */
1722+ public long getMultiClickThreshhold() {
1723+ return multiClickThreshhold;
1724+ }
1725+
1726+ /**
1727+ * Returns the model that this button represents.
1728+ * @return the <code>model</code> property
1729+ * @see #setModel
1730+ */
1731+ public ButtonModel getModel() {
1732+ return model;
1733+ }
1734+
1735+ /**
1736+ * Sets the model that this button represents.
1737+ * @param newModel the new <code>ButtonModel</code>
1738+ * @see #getModel
1739+ * @beaninfo
1740+ * bound: true
1741+ * description: Model that the Button uses.
1742+ */
1743+ public void setModel(ButtonModel newModel) {
1744+
1745+ ButtonModel oldModel = getModel();
1746+
1747+ if (oldModel != null) {
1748+ oldModel.removeChangeListener(changeListener);
1749+ oldModel.removeActionListener(actionListener);
1750+ oldModel.removeItemListener(itemListener);
1751+ changeListener = null;
1752+ actionListener = null;
1753+ itemListener = null;
1754+ }
1755+
1756+ model = newModel;
1757+
1758+ if (newModel != null) {
1759+ changeListener = createChangeListener();
1760+ actionListener = createActionListener();
1761+ itemListener = createItemListener();
1762+ newModel.addChangeListener(changeListener);
1763+ newModel.addActionListener(actionListener);
1764+ newModel.addItemListener(itemListener);
1765+
1766+ updateMnemonicProperties();
1767+ //We invoke setEnabled() from JComponent
1768+ //because setModel() can be called from a constructor
1769+ //when the button is not fully initialized
1770+ super.setEnabled(newModel.isEnabled());
1771+
1772+ } else {
1773+ mnemonic = '\0';
1774+ }
1775+
1776+ updateDisplayedMnemonicIndex(getText(), mnemonic);
1777+
1778+ firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
1779+ if (newModel != oldModel) {
1780+ revalidate();
1781+ repaint();
1782+ }
1783+ }
1784+
1785+
1786+ /**
1787+ * Returns the L&F object that renders this component.
1788+ * @return the ButtonUI object
1789+ * @see #setUI
1790+ */
1791+ public ButtonUI getUI() {
1792+ return (ButtonUI) ui;
1793+ }
1794+
1795+
1796+ /**
1797+ * Sets the L&F object that renders this component.
1798+ * @param ui the <code>ButtonUI</code> L&F object
1799+ * @see #getUI
1800+ * @beaninfo
1801+ * bound: true
1802+ * hidden: true
1803+ * attribute: visualUpdate true
1804+ * description: The UI object that implements the LookAndFeel.
1805+ */
1806+ public void setUI(ButtonUI ui) {
1807+ super.setUI(ui);
1808+ // disabled icons are generated by the LF so they should be unset here
1809+ if (disabledIcon instanceof UIResource) {
1810+ setDisabledIcon(null);
1811+ }
1812+ if (disabledSelectedIcon instanceof UIResource) {
1813+ setDisabledSelectedIcon(null);
1814+ }
1815+ }
1816+
1817+
1818+ /**
1819+ * Resets the UI property to a value from the current look
1820+ * and feel. Subtypes of <code>AbstractButton</code>
1821+ * should override this to update the UI. For
1822+ * example, <code>JButton</code> might do the following:
1823+ * <pre>
1824+ * setUI((ButtonUI)UIManager.getUI(
1825+ * "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
1826+ * </pre>
1827+ */
1828+ public void updateUI() {
1829+ }
1830+
1831+ /**
1832+ * Adds the specified component to this container at the specified
1833+ * index, refer to
1834+ * {@link java.awt.Container#addImpl(Component, Object, int)}
1835+ * for a complete description of this method.
1836+ *
1837+ * @param comp the component to be added
1838+ * @param constraints an object expressing layout constraints
1839+ * for this component
1840+ * @param index the position in the container's list at which to
1841+ * insert the component, where <code>-1</code>
1842+ * means append to the end
1843+ * @exception IllegalArgumentException if <code>index</code> is invalid
1844+ * @exception IllegalArgumentException if adding the container's parent
1845+ * to itself
1846+ * @exception IllegalArgumentException if adding a window to a container
1847+ * @since 1.5
1848+ */
1849+ protected void addImpl(Component comp, Object constraints, int index) {
1850+ if (!setLayout) {
1851+ setLayout(new OverlayLayout(this));
1852+ }
1853+ super.addImpl(comp, constraints, index);
1854+ }
1855+
1856+ /**
1857+ * Sets the layout manager for this container, refer to
1858+ * {@link java.awt.Container#setLayout(LayoutManager)}
1859+ * for a complete description of this method.
1860+ *
1861+ * @param mgr the specified layout manager
1862+ * @since 1.5
1863+ */
1864+ public void setLayout(LayoutManager mgr) {
1865+ setLayout = true;
1866+ super.setLayout(mgr);
1867+ }
1868+
1869+ /**
1870+ * Adds a <code>ChangeListener</code> to the button.
1871+ * @param l the listener to be added
1872+ */
1873+ public void addChangeListener(ChangeListener l) {
1874+ listenerList.add(ChangeListener.class, l);
1875+ }
1876+
1877+ /**
1878+ * Removes a ChangeListener from the button.
1879+ * @param l the listener to be removed
1880+ */
1881+ public void removeChangeListener(ChangeListener l) {
1882+ listenerList.remove(ChangeListener.class, l);
1883+ }
1884+
1885+ /**
1886+ * Returns an array of all the <code>ChangeListener</code>s added
1887+ * to this AbstractButton with addChangeListener().
1888+ *
1889+ * @return all of the <code>ChangeListener</code>s added or an empty
1890+ * array if no listeners have been added
1891+ * @since 1.4
1892+ */
1893+ public ChangeListener[] getChangeListeners() {
1894+ return listenerList.getListeners(ChangeListener.class);
1895+ }
1896+
1897+ /**
1898+ * Notifies all listeners that have registered interest for
1899+ * notification on this event type. The event instance
1900+ * is lazily created.
1901+ * @see EventListenerList
1902+ */
1903+ protected void fireStateChanged() {
1904+ // Guaranteed to return a non-null array
1905+ Object[] listeners = listenerList.getListenerList();
1906+ // Process the listeners last to first, notifying
1907+ // those that are interested in this event
1908+ for (int i = listeners.length-2; i>=0; i-=2) {
1909+ if (listeners[i]==ChangeListener.class) {
1910+ // Lazily create the event:
1911+ if (changeEvent == null)
1912+ changeEvent = new ChangeEvent(this);
1913+ ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
1914+ }
1915+ }
1916+ }
1917+
1918+ /**
1919+ * Adds an <code>ActionListener</code> to the button.
1920+ * @param l the <code>ActionListener</code> to be added
1921+ */
1922+ public void addActionListener(ActionListener l) {
1923+ listenerList.add(ActionListener.class, l);
1924+ }
1925+
1926+ /**
1927+ * Removes an <code>ActionListener</code> from the button.
1928+ * If the listener is the currently set <code>Action</code>
1929+ * for the button, then the <code>Action</code>
1930+ * is set to <code>null</code>.
1931+ *
1932+ * @param l the listener to be removed
1933+ */
1934+ public void removeActionListener(ActionListener l) {
1935+ if ((l != null) && (getAction() == l)) {
1936+ setAction(null);
1937+ } else {
1938+ listenerList.remove(ActionListener.class, l);
1939+ }
1940+ }
1941+
1942+ /**
1943+ * Returns an array of all the <code>ActionListener</code>s added
1944+ * to this AbstractButton with addActionListener().
1945+ *
1946+ * @return all of the <code>ActionListener</code>s added or an empty
1947+ * array if no listeners have been added
1948+ * @since 1.4
1949+ */
1950+ public ActionListener[] getActionListeners() {
1951+ return listenerList.getListeners(ActionListener.class);
1952+ }
1953+
1954+ /**
1955+ * Subclasses that want to handle <code>ChangeEvents</code> differently
1956+ * can override this to return another <code>ChangeListener</code>
1957+ * implementation.
1958+ *
1959+ * @return the new <code>ChangeListener</code>
1960+ */
1961+ protected ChangeListener createChangeListener() {
1962+ return getHandler();
1963+ }
1964+
1965+ /**
1966+ * Extends <code>ChangeListener</code> to be serializable.
1967+ * <p>
1968+ * <strong>Warning:</strong>
1969+ * Serialized objects of this class will not be compatible with
1970+ * future Swing releases. The current serialization support is
1971+ * appropriate for short term storage or RMI between applications running
1972+ * the same version of Swing. As of 1.4, support for long term storage
1973+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
1974+ * has been added to the <code>java.beans</code> package.
1975+ * Please see {@link java.beans.XMLEncoder}.
1976+ */
1977+ protected class ButtonChangeListener implements ChangeListener, Serializable {
1978+ // NOTE: This class is NOT used, instead the functionality has
1979+ // been moved to Handler.
1980+ ButtonChangeListener() {
1981+ }
1982+
1983+ public void stateChanged(ChangeEvent e) {
1984+ getHandler().stateChanged(e);
1985+ }
1986+ }
1987+
1988+
1989+ /**
1990+ * Notifies all listeners that have registered interest for
1991+ * notification on this event type. The event instance
1992+ * is lazily created using the <code>event</code>
1993+ * parameter.
1994+ *
1995+ * @param event the <code>ActionEvent</code> object
1996+ * @see EventListenerList
1997+ */
1998+ protected void fireActionPerformed(ActionEvent event) {
1999+ // Guaranteed to return a non-null array
2000+ Object[] listeners = listenerList.getListenerList();
2001+ ActionEvent e = null;
2002+ // Process the listeners last to first, notifying
2003+ // those that are interested in this event
2004+ for (int i = listeners.length-2; i>=0; i-=2) {
2005+ if (listeners[i]==ActionListener.class) {
2006+ // Lazily create the event:
2007+ if (e == null) {
2008+ String actionCommand = event.getActionCommand();
2009+ if(actionCommand == null) {
2010+ actionCommand = getActionCommand();
2011+ }
2012+ e = new ActionEvent(AbstractButton.this,
2013+ ActionEvent.ACTION_PERFORMED,
2014+ actionCommand,
2015+ event.getWhen(),
2016+ event.getModifiers());
2017+ }
2018+ ((ActionListener)listeners[i+1]).actionPerformed(e);
2019+ }
2020+ }
2021+ }
2022+
2023+ /**
2024+ * Notifies all listeners that have registered interest for
2025+ * notification on this event type. The event instance
2026+ * is lazily created using the <code>event</code> parameter.
2027+ *
2028+ * @param event the <code>ItemEvent</code> object
2029+ * @see EventListenerList
2030+ */
2031+ protected void fireItemStateChanged(ItemEvent event) {
2032+ // Guaranteed to return a non-null array
2033+ Object[] listeners = listenerList.getListenerList();
2034+ ItemEvent e = null;
2035+ // Process the listeners last to first, notifying
2036+ // those that are interested in this event
2037+ for (int i = listeners.length-2; i>=0; i-=2) {
2038+ if (listeners[i]==ItemListener.class) {
2039+ // Lazily create the event:
2040+ if (e == null) {
2041+ e = new ItemEvent(AbstractButton.this,
2042+ ItemEvent.ITEM_STATE_CHANGED,
2043+ AbstractButton.this,
2044+ event.getStateChange());
2045+ }
2046+ ((ItemListener)listeners[i+1]).itemStateChanged(e);
2047+ }
2048+ }
2049+ if (accessibleContext != null) {
2050+ if (event.getStateChange() == ItemEvent.SELECTED) {
2051+ accessibleContext.firePropertyChange(
2052+ AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2053+ null, AccessibleState.SELECTED);
2054+ accessibleContext.firePropertyChange(
2055+ AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
2056+ Integer.valueOf(0), Integer.valueOf(1));
2057+ } else {
2058+ accessibleContext.firePropertyChange(
2059+ AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2060+ AccessibleState.SELECTED, null);
2061+ accessibleContext.firePropertyChange(
2062+ AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
2063+ Integer.valueOf(1), Integer.valueOf(0));
2064+ }
2065+ }
2066+ }
2067+
2068+
2069+ protected ActionListener createActionListener() {
2070+ return getHandler();
2071+ }
2072+
2073+
2074+ protected ItemListener createItemListener() {
2075+ return getHandler();
2076+ }
2077+
2078+
2079+ /**
2080+ * Enables (or disables) the button.
2081+ * @param b true to enable the button, otherwise false
2082+ */
2083+ public void setEnabled(boolean b) {
2084+ if (!b && model.isRollover()) {
2085+ model.setRollover(false);
2086+ }
2087+ super.setEnabled(b);
2088+ model.setEnabled(b);
2089+ }
2090+
2091+ // *** Deprecated java.awt.Button APIs below *** //
2092+
2093+ /**
2094+ * Returns the label text.
2095+ *
2096+ * @return a <code>String</code> containing the label
2097+ * @deprecated - Replaced by <code>getText</code>
2098+ */
2099+ @Deprecated
2100+ public String getLabel() {
2101+ return getText();
2102+ }
2103+
2104+ /**
2105+ * Sets the label text.
2106+ *
2107+ * @param label a <code>String</code> containing the text
2108+ * @deprecated - Replaced by <code>setText(text)</code>
2109+ * @beaninfo
2110+ * bound: true
2111+ * description: Replace by setText(text)
2112+ */
2113+ @Deprecated
2114+ public void setLabel(String label) {
2115+ setText(label);
2116+ }
2117+
2118+ /**
2119+ * Adds an <code>ItemListener</code> to the <code>checkbox</code>.
2120+ * @param l the <code>ItemListener</code> to be added
2121+ */
2122+ public void addItemListener(ItemListener l) {
2123+ listenerList.add(ItemListener.class, l);
2124+ }
2125+
2126+ /**
2127+ * Removes an <code>ItemListener</code> from the button.
2128+ * @param l the <code>ItemListener</code> to be removed
2129+ */
2130+ public void removeItemListener(ItemListener l) {
2131+ listenerList.remove(ItemListener.class, l);
2132+ }
2133+
2134+ /**
2135+ * Returns an array of all the <code>ItemListener</code>s added
2136+ * to this AbstractButton with addItemListener().
2137+ *
2138+ * @return all of the <code>ItemListener</code>s added or an empty
2139+ * array if no listeners have been added
2140+ * @since 1.4
2141+ */
2142+ public ItemListener[] getItemListeners() {
2143+ return listenerList.getListeners(ItemListener.class);
2144+ }
2145+
2146+ /**
2147+ * Returns an array (length 1) containing the label or
2148+ * <code>null</code> if the button is not selected.
2149+ *
2150+ * @return an array containing 1 Object: the text of the button,
2151+ * if the item is selected; otherwise <code>null</code>
2152+ */
2153+ public Object[] getSelectedObjects() {
2154+ if (isSelected() == false) {
2155+ return null;
2156+ }
2157+ Object[] selectedObjects = new Object[1];
2158+ selectedObjects[0] = getText();
2159+ return selectedObjects;
2160+ }
2161+
2162+ protected void init(String text, Icon icon) {
2163+ if(text != null) {
2164+ setText(text);
2165+ }
2166+
2167+ if(icon != null) {
2168+ setIcon(icon);
2169+ }
2170+
2171+ // Set the UI
2172+ updateUI();
2173+
2174+ setAlignmentX(LEFT_ALIGNMENT);
2175+ setAlignmentY(CENTER_ALIGNMENT);
2176+ }
2177+
2178+
2179+ /**
2180+ * This is overridden to return false if the current <code>Icon</code>'s
2181+ * <code>Image</code> is not equal to the
2182+ * passed in <code>Image</code> <code>img</code>.
2183+ *
2184+ * @param img the <code>Image</code> to be compared
2185+ * @param infoflags flags used to repaint the button when the image
2186+ * is updated and which determine how much is to be painted
2187+ * @param x the x coordinate
2188+ * @param y the y coordinate
2189+ * @param w the width
2190+ * @param h the height
2191+ * @see java.awt.image.ImageObserver
2192+ * @see java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int, int)
2193+ */
2194+ public boolean imageUpdate(Image img, int infoflags,
2195+ int x, int y, int w, int h) {
2196+ Icon iconDisplayed = getIcon();
2197+ if (iconDisplayed == null) {
2198+ return false;
2199+ }
2200+
2201+ if (!model.isEnabled()) {
2202+ if (model.isSelected()) {
2203+ iconDisplayed = getDisabledSelectedIcon();
2204+ } else {
2205+ iconDisplayed = getDisabledIcon();
2206+ }
2207+ } else if (model.isPressed() && model.isArmed()) {
2208+ iconDisplayed = getPressedIcon();
2209+ } else if (isRolloverEnabled() && model.isRollover()) {
2210+ if (model.isSelected()) {
2211+ iconDisplayed = getRolloverSelectedIcon();
2212+ } else {
2213+ iconDisplayed = getRolloverIcon();
2214+ }
2215+ } else if (model.isSelected()) {
2216+ iconDisplayed = getSelectedIcon();
2217+ }
2218+
2219+ if (!SwingUtilities.doesIconReferenceImage(iconDisplayed, img)) {
2220+ // We don't know about this image, disable the notification so
2221+ // we don't keep repainting.
2222+ return false;
2223+ }
2224+ return super.imageUpdate(img, infoflags, x, y, w, h);
2225+ }
2226+
2227+ void setUIProperty(String propertyName, Object value) {
2228+ if (propertyName == "borderPainted") {
2229+ if (!borderPaintedSet) {
2230+ setBorderPainted(((Boolean)value).booleanValue());
2231+ borderPaintedSet = false;
2232+ }
2233+ } else if (propertyName == "rolloverEnabled") {
2234+ if (!rolloverEnabledSet) {
2235+ setRolloverEnabled(((Boolean)value).booleanValue());
2236+ rolloverEnabledSet = false;
2237+ }
2238+ } else if (propertyName == "iconTextGap") {
2239+ if (!iconTextGapSet) {
2240+ setIconTextGap(((Number)value).intValue());
2241+ iconTextGapSet = false;
2242+ }
2243+ } else if (propertyName == "contentAreaFilled") {
2244+ if (!contentAreaFilledSet) {
2245+ setContentAreaFilled(((Boolean)value).booleanValue());
2246+ contentAreaFilledSet = false;
2247+ }
2248+ } else {
2249+ super.setUIProperty(propertyName, value);
2250+ }
2251+ }
2252+
2253+ /**
2254+ * Returns a string representation of this <code>AbstractButton</code>.
2255+ * This method
2256+ * is intended to be used only for debugging purposes, and the
2257+ * content and format of the returned string may vary between
2258+ * implementations. The returned string may be empty but may not
2259+ * be <code>null</code>.
2260+ * <P>
2261+ * Overriding <code>paramString</code> to provide information about the
2262+ * specific new aspects of the JFC components.
2263+ *
2264+ * @return a string representation of this <code>AbstractButton</code>
2265+ */
2266+ protected String paramString() {
2267+ String defaultIconString = ((defaultIcon != null)
2268+ && (defaultIcon != this) ?
2269+ defaultIcon.toString() : "");
2270+ String pressedIconString = ((pressedIcon != null)
2271+ && (pressedIcon != this) ?
2272+ pressedIcon.toString() : "");
2273+ String disabledIconString = ((disabledIcon != null)
2274+ && (disabledIcon != this) ?
2275+ disabledIcon.toString() : "");
2276+ String selectedIconString = ((selectedIcon != null)
2277+ && (selectedIcon != this) ?
2278+ selectedIcon.toString() : "");
2279+ String disabledSelectedIconString = ((disabledSelectedIcon != null) &&
2280+ (disabledSelectedIcon != this) ?
2281+ disabledSelectedIcon.toString()
2282+ : "");
2283+ String rolloverIconString = ((rolloverIcon != null)
2284+ && (rolloverIcon != this) ?
2285+ rolloverIcon.toString() : "");
2286+ String rolloverSelectedIconString = ((rolloverSelectedIcon != null) &&
2287+ (rolloverSelectedIcon != this) ?
2288+ rolloverSelectedIcon.toString()
2289+ : "");
2290+ String paintBorderString = (paintBorder ? "true" : "false");
2291+ String paintFocusString = (paintFocus ? "true" : "false");
2292+ String rolloverEnabledString = (rolloverEnabled ? "true" : "false");
2293+
2294+ return super.paramString() +
2295+ ",defaultIcon=" + defaultIconString +
2296+ ",disabledIcon=" + disabledIconString +
2297+ ",disabledSelectedIcon=" + disabledSelectedIconString +
2298+ ",margin=" + margin +
2299+ ",paintBorder=" + paintBorderString +
2300+ ",paintFocus=" + paintFocusString +
2301+ ",pressedIcon=" + pressedIconString +
2302+ ",rolloverEnabled=" + rolloverEnabledString +
2303+ ",rolloverIcon=" + rolloverIconString +
2304+ ",rolloverSelectedIcon=" + rolloverSelectedIconString +
2305+ ",selectedIcon=" + selectedIconString +
2306+ ",text=" + text;
2307+ }
2308+
2309+
2310+ private Handler getHandler() {
2311+ if (handler == null) {
2312+ handler = new Handler();
2313+ }
2314+ return handler;
2315+ }
2316+
2317+
2318+ //
2319+ // Listeners that are added to model
2320+ //
2321+ class Handler implements ActionListener, ChangeListener, ItemListener,
2322+ Serializable {
2323+ //
2324+ // ChangeListener
2325+ //
2326+ public void stateChanged(ChangeEvent e) {
2327+ Object source = e.getSource();
2328+
2329+ updateMnemonicProperties();
2330+ if (isEnabled() != model.isEnabled()) {
2331+ setEnabled(model.isEnabled());
2332+ }
2333+ fireStateChanged();
2334+ repaint();
2335+ }
2336+
2337+ //
2338+ // ActionListener
2339+ //
2340+ public void actionPerformed(ActionEvent event) {
2341+ fireActionPerformed(event);
2342+ }
2343+
2344+ //
2345+ // ItemListener
2346+ //
2347+ public void itemStateChanged(ItemEvent event) {
2348+ fireItemStateChanged(event);
2349+ if (shouldUpdateSelectedStateFromAction()) {
2350+ Action action = getAction();
2351+ if (action != null && AbstractAction.hasSelectedKey(action)) {
2352+ boolean selected = isSelected();
2353+ boolean isActionSelected = AbstractAction.isSelected(
2354+ action);
2355+ if (isActionSelected != selected) {
2356+ action.putValue(Action.SELECTED_KEY, selected);
2357+ }
2358+ }
2359+ }
2360+ }
2361+ }
2362+
2363+///////////////////
2364+// Accessibility support
2365+///////////////////
2366+ /**
2367+ * This class implements accessibility support for the
2368+ * <code>AbstractButton</code> class. It provides an implementation of the
2369+ * Java Accessibility API appropriate to button and menu item
2370+ * user-interface elements.
2371+ * <p>
2372+ * <strong>Warning:</strong>
2373+ * Serialized objects of this class will not be compatible with
2374+ * future Swing releases. The current serialization support is
2375+ * appropriate for short term storage or RMI between applications running
2376+ * the same version of Swing. As of 1.4, support for long term storage
2377+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
2378+ * has been added to the <code>java.beans</code> package.
2379+ * Please see {@link java.beans.XMLEncoder}.
2380+ * @since 1.4
2381+ */
2382+ protected abstract class AccessibleAbstractButton
2383+ extends AccessibleJComponent implements AccessibleAction,
2384+ AccessibleValue, AccessibleText, AccessibleExtendedComponent {
2385+
2386+ /**
2387+ * Returns the accessible name of this object.
2388+ *
2389+ * @return the localized name of the object -- can be
2390+ * <code>null</code> if this
2391+ * object does not have a name
2392+ */
2393+ public String getAccessibleName() {
2394+ String name = accessibleName;
2395+
2396+ if (name == null) {
2397+ name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
2398+ }
2399+ if (name == null) {
2400+ name = AbstractButton.this.getText();
2401+ }
2402+ if (name == null) {
2403+ name = super.getAccessibleName();
2404+ }
2405+ return name;
2406+ }
2407+
2408+ /**
2409+ * Get the AccessibleIcons associated with this object if one
2410+ * or more exist. Otherwise return null.
2411+ * @since 1.3
2412+ */
2413+ public AccessibleIcon [] getAccessibleIcon() {
2414+ Icon defaultIcon = getIcon();
2415+
2416+ if (defaultIcon instanceof Accessible) {
2417+ AccessibleContext ac =
2418+ ((Accessible)defaultIcon).getAccessibleContext();
2419+ if (ac != null && ac instanceof AccessibleIcon) {
2420+ return new AccessibleIcon[] { (AccessibleIcon)ac };
2421+ }
2422+ }
2423+ return null;
2424+ }
2425+
2426+ /**
2427+ * Get the state set of this object.
2428+ *
2429+ * @return an instance of AccessibleState containing the current state
2430+ * of the object
2431+ * @see AccessibleState
2432+ */
2433+ public AccessibleStateSet getAccessibleStateSet() {
2434+ AccessibleStateSet states = super.getAccessibleStateSet();
2435+ if (getModel().isArmed()) {
2436+ states.add(AccessibleState.ARMED);
2437+ }
2438+ if (isFocusOwner()) {
2439+ states.add(AccessibleState.FOCUSED);
2440+ }
2441+ if (getModel().isPressed()) {
2442+ states.add(AccessibleState.PRESSED);
2443+ }
2444+ if (isSelected()) {
2445+ states.add(AccessibleState.CHECKED);
2446+ }
2447+ return states;
2448+ }
2449+
2450+ /**
2451+ * Get the AccessibleRelationSet associated with this object if one
2452+ * exists. Otherwise return null.
2453+ * @see AccessibleRelation
2454+ * @since 1.3
2455+ */
2456+ public AccessibleRelationSet getAccessibleRelationSet() {
2457+
2458+ // Check where the AccessibleContext's relation
2459+ // set already contains a MEMBER_OF relation.
2460+ AccessibleRelationSet relationSet
2461+ = super.getAccessibleRelationSet();
2462+
2463+ if (!relationSet.contains(AccessibleRelation.MEMBER_OF)) {
2464+ // get the members of the button group if one exists
2465+ ButtonModel model = getModel();
2466+ if (model != null && model instanceof DefaultButtonModel) {
2467+ ButtonGroup group = ((DefaultButtonModel)model).getGroup();
2468+ if (group != null) {
2469+ // set the target of the MEMBER_OF relation to be
2470+ // the members of the button group.
2471+ int len = group.getButtonCount();
2472+ Object [] target = new Object[len];
2473+ Enumeration elem = group.getElements();
2474+ for (int i = 0; i < len; i++) {
2475+ if (elem.hasMoreElements()) {
2476+ target[i] = elem.nextElement();
2477+ }
2478+ }
2479+ AccessibleRelation relation =
2480+ new AccessibleRelation(AccessibleRelation.MEMBER_OF);
2481+ relation.setTarget(target);
2482+ relationSet.add(relation);
2483+ }
2484+ }
2485+ }
2486+ return relationSet;
2487+ }
2488+
2489+ /**
2490+ * Get the AccessibleAction associated with this object. In the
2491+ * implementation of the Java Accessibility API for this class,
2492+ * return this object, which is responsible for implementing the
2493+ * AccessibleAction interface on behalf of itself.
2494+ *
2495+ * @return this object
2496+ */
2497+ public AccessibleAction getAccessibleAction() {
2498+ return this;
2499+ }
2500+
2501+ /**
2502+ * Get the AccessibleValue associated with this object. In the
2503+ * implementation of the Java Accessibility API for this class,
2504+ * return this object, which is responsible for implementing the
2505+ * AccessibleValue interface on behalf of itself.
2506+ *
2507+ * @return this object
2508+ */
2509+ public AccessibleValue getAccessibleValue() {
2510+ return this;
2511+ }
2512+
2513+ /**
2514+ * Returns the number of Actions available in this object. The
2515+ * default behavior of a button is to have one action - toggle
2516+ * the button.
2517+ *
2518+ * @return 1, the number of Actions in this object
2519+ */
2520+ public int getAccessibleActionCount() {
2521+ return 1;
2522+ }
2523+
2524+ /**
2525+ * Return a description of the specified action of the object.
2526+ *
2527+ * @param i zero-based index of the actions
2528+ */
2529+ public String getAccessibleActionDescription(int i) {
2530+ if (i == 0) {
2531+ return UIManager.getString("AbstractButton.clickText");
2532+ } else {
2533+ return null;
2534+ }
2535+ }
2536+
2537+ /**
2538+ * Perform the specified Action on the object
2539+ *
2540+ * @param i zero-based index of actions
2541+ * @return true if the the action was performed; else false.
2542+ */
2543+ public boolean doAccessibleAction(int i) {
2544+ if (i == 0) {
2545+ doClick();
2546+ return true;
2547+ } else {
2548+ return false;
2549+ }
2550+ }
2551+
2552+ /**
2553+ * Get the value of this object as a Number.
2554+ *
2555+ * @return An Integer of 0 if this isn't selected or an Integer of 1 if
2556+ * this is selected.
2557+ * @see AbstractButton#isSelected
2558+ */
2559+ public Number getCurrentAccessibleValue() {
2560+ if (isSelected()) {
2561+ return Integer.valueOf(1);
2562+ } else {
2563+ return Integer.valueOf(0);
2564+ }
2565+ }
2566+
2567+ /**
2568+ * Set the value of this object as a Number.
2569+ *
2570+ * @return True if the value was set.
2571+ */
2572+ public boolean setCurrentAccessibleValue(Number n) {
2573+ // TIGER - 4422535
2574+ if (n == null) {
2575+ return false;
2576+ }
2577+ int i = n.intValue();
2578+ if (i == 0) {
2579+ setSelected(false);
2580+ } else {
2581+ setSelected(true);
2582+ }
2583+ return true;
2584+ }
2585+
2586+ /**
2587+ * Get the minimum value of this object as a Number.
2588+ *
2589+ * @return an Integer of 0.
2590+ */
2591+ public Number getMinimumAccessibleValue() {
2592+ return Integer.valueOf(0);
2593+ }
2594+
2595+ /**
2596+ * Get the maximum value of this object as a Number.
2597+ *
2598+ * @return An Integer of 1.
2599+ */
2600+ public Number getMaximumAccessibleValue() {
2601+ return Integer.valueOf(1);
2602+ }
2603+
2604+
2605+ /* AccessibleText ---------- */
2606+
2607+ public AccessibleText getAccessibleText() {
2608+ View view = (View)AbstractButton.this.getClientProperty("html");
2609+ if (view != null) {
2610+ return this;
2611+ } else {
2612+ return null;
2613+ }
2614+ }
2615+
2616+ /**
2617+ * Given a point in local coordinates, return the zero-based index
2618+ * of the character under that Point. If the point is invalid,
2619+ * this method returns -1.
2620+ *
2621+ * Note: the AbstractButton must have a valid size (e.g. have
2622+ * been added to a parent container whose ancestor container
2623+ * is a valid top-level window) for this method to be able
2624+ * to return a meaningful value.
2625+ *
2626+ * @param p the Point in local coordinates
2627+ * @return the zero-based index of the character under Point p; if
2628+ * Point is invalid returns -1.
2629+ * @since 1.3
2630+ */
2631+ public int getIndexAtPoint(Point p) {
2632+ View view = (View) AbstractButton.this.getClientProperty("html");
2633+ if (view != null) {
2634+ Rectangle r = getTextRectangle();
2635+ if (r == null) {
2636+ return -1;
2637+ }
2638+ Rectangle2D.Float shape =
2639+ new Rectangle2D.Float(r.x, r.y, r.width, r.height);
2640+ Position.Bias bias[] = new Position.Bias[1];
2641+ return view.viewToModel(p.x, p.y, shape, bias);
2642+ } else {
2643+ return -1;
2644+ }
2645+ }
2646+
2647+ /**
2648+ * Determine the bounding box of the character at the given
2649+ * index into the string. The bounds are returned in local
2650+ * coordinates. If the index is invalid an empty rectangle is
2651+ * returned.
2652+ *
2653+ * Note: the AbstractButton must have a valid size (e.g. have
2654+ * been added to a parent container whose ancestor container
2655+ * is a valid top-level window) for this method to be able
2656+ * to return a meaningful value.
2657+ *
2658+ * @param i the index into the String
2659+ * @return the screen coordinates of the character's the bounding box,
2660+ * if index is invalid returns an empty rectangle.
2661+ * @since 1.3
2662+ */
2663+ public Rectangle getCharacterBounds(int i) {
2664+ View view = (View) AbstractButton.this.getClientProperty("html");
2665+ if (view != null) {
2666+ Rectangle r = getTextRectangle();
2667+ if (r == null) {
2668+ return null;
2669+ }
2670+ Rectangle2D.Float shape =
2671+ new Rectangle2D.Float(r.x, r.y, r.width, r.height);
2672+ try {
2673+ Shape charShape =
2674+ view.modelToView(i, shape, Position.Bias.Forward);
2675+ return charShape.getBounds();
2676+ } catch (BadLocationException e) {
2677+ return null;
2678+ }
2679+ } else {
2680+ return null;
2681+ }
2682+ }
2683+
2684+ /**
2685+ * Return the number of characters (valid indicies)
2686+ *
2687+ * @return the number of characters
2688+ * @since 1.3
2689+ */
2690+ public int getCharCount() {
2691+ View view = (View) AbstractButton.this.getClientProperty("html");
2692+ if (view != null) {
2693+ Document d = view.getDocument();
2694+ if (d instanceof StyledDocument) {
2695+ StyledDocument doc = (StyledDocument)d;
2696+ return doc.getLength();
2697+ }
2698+ }
2699+ return accessibleContext.getAccessibleName().length();
2700+ }
2701+
2702+ /**
2703+ * Return the zero-based offset of the caret.
2704+ *
2705+ * Note: That to the right of the caret will have the same index
2706+ * value as the offset (the caret is between two characters).
2707+ * @return the zero-based offset of the caret.
2708+ * @since 1.3
2709+ */
2710+ public int getCaretPosition() {
2711+ // There is no caret.
2712+ return -1;
2713+ }
2714+
2715+ /**
2716+ * Returns the String at a given index.
2717+ *
2718+ * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
2719+ * or AccessibleText.SENTENCE to retrieve
2720+ * @param index an index within the text >= 0
2721+ * @return the letter, word, or sentence,
2722+ * null for an invalid index or part
2723+ * @since 1.3
2724+ */
2725+ public String getAtIndex(int part, int index) {
2726+ if (index < 0 || index >= getCharCount()) {
2727+ return null;
2728+ }
2729+ switch (part) {
2730+ case AccessibleText.CHARACTER:
2731+ try {
2732+ return getText(index, 1);
2733+ } catch (BadLocationException e) {
2734+ return null;
2735+ }
2736+ case AccessibleText.WORD:
2737+ try {
2738+ String s = getText(0, getCharCount());
2739+ BreakIterator words = BreakIterator.getWordInstance(getLocale());
2740+ words.setText(s);
2741+ int end = words.following(index);
2742+ return s.substring(words.previous(), end);
2743+ } catch (BadLocationException e) {
2744+ return null;
2745+ }
2746+ case AccessibleText.SENTENCE:
2747+ try {
2748+ String s = getText(0, getCharCount());
2749+ BreakIterator sentence =
2750+ BreakIterator.getSentenceInstance(getLocale());
2751+ sentence.setText(s);
2752+ int end = sentence.following(index);
2753+ return s.substring(sentence.previous(), end);
2754+ } catch (BadLocationException e) {
2755+ return null;
2756+ }
2757+ default:
2758+ return null;
2759+ }
2760+ }
2761+
2762+ /**
2763+ * Returns the String after a given index.
2764+ *
2765+ * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
2766+ * or AccessibleText.SENTENCE to retrieve
2767+ * @param index an index within the text >= 0
2768+ * @return the letter, word, or sentence, null for an invalid
2769+ * index or part
2770+ * @since 1.3
2771+ */
2772+ public String getAfterIndex(int part, int index) {
2773+ if (index < 0 || index >= getCharCount()) {
2774+ return null;
2775+ }
2776+ switch (part) {
2777+ case AccessibleText.CHARACTER:
2778+ if (index+1 >= getCharCount()) {
2779+ return null;
2780+ }
2781+ try {
2782+ return getText(index+1, 1);
2783+ } catch (BadLocationException e) {
2784+ return null;
2785+ }
2786+ case AccessibleText.WORD:
2787+ try {
2788+ String s = getText(0, getCharCount());
2789+ BreakIterator words = BreakIterator.getWordInstance(getLocale());
2790+ words.setText(s);
2791+ int start = words.following(index);
2792+ if (start == BreakIterator.DONE || start >= s.length()) {
2793+ return null;
2794+ }
2795+ int end = words.following(start);
2796+ if (end == BreakIterator.DONE || end >= s.length()) {
2797+ return null;
2798+ }
2799+ return s.substring(start, end);
2800+ } catch (BadLocationException e) {
2801+ return null;
2802+ }
2803+ case AccessibleText.SENTENCE:
2804+ try {
2805+ String s = getText(0, getCharCount());
2806+ BreakIterator sentence =
2807+ BreakIterator.getSentenceInstance(getLocale());
2808+ sentence.setText(s);
2809+ int start = sentence.following(index);
2810+ if (start == BreakIterator.DONE || start > s.length()) {
2811+ return null;
2812+ }
2813+ int end = sentence.following(start);
2814+ if (end == BreakIterator.DONE || end > s.length()) {
2815+ return null;
2816+ }
2817+ return s.substring(start, end);
2818+ } catch (BadLocationException e) {
2819+ return null;
2820+ }
2821+ default:
2822+ return null;
2823+ }
2824+ }
2825+
2826+ /**
2827+ * Returns the String before a given index.
2828+ *
2829+ * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
2830+ * or AccessibleText.SENTENCE to retrieve
2831+ * @param index an index within the text >= 0
2832+ * @return the letter, word, or sentence, null for an invalid index
2833+ * or part
2834+ * @since 1.3
2835+ */
2836+ public String getBeforeIndex(int part, int index) {
2837+ if (index < 0 || index > getCharCount()-1) {
2838+ return null;
2839+ }
2840+ switch (part) {
2841+ case AccessibleText.CHARACTER:
2842+ if (index == 0) {
2843+ return null;
2844+ }
2845+ try {
2846+ return getText(index-1, 1);
2847+ } catch (BadLocationException e) {
2848+ return null;
2849+ }
2850+ case AccessibleText.WORD:
2851+ try {
2852+ String s = getText(0, getCharCount());
2853+ BreakIterator words = BreakIterator.getWordInstance(getLocale());
2854+ words.setText(s);
2855+ int end = words.following(index);
2856+ end = words.previous();
2857+ int start = words.previous();
2858+ if (start == BreakIterator.DONE) {
2859+ return null;
2860+ }
2861+ return s.substring(start, end);
2862+ } catch (BadLocationException e) {
2863+ return null;
2864+ }
2865+ case AccessibleText.SENTENCE:
2866+ try {
2867+ String s = getText(0, getCharCount());
2868+ BreakIterator sentence =
2869+ BreakIterator.getSentenceInstance(getLocale());
2870+ sentence.setText(s);
2871+ int end = sentence.following(index);
2872+ end = sentence.previous();
2873+ int start = sentence.previous();
2874+ if (start == BreakIterator.DONE) {
2875+ return null;
2876+ }
2877+ return s.substring(start, end);
2878+ } catch (BadLocationException e) {
2879+ return null;
2880+ }
2881+ default:
2882+ return null;
2883+ }
2884+ }
2885+
2886+ /**
2887+ * Return the AttributeSet for a given character at a given index
2888+ *
2889+ * @param i the zero-based index into the text
2890+ * @return the AttributeSet of the character
2891+ * @since 1.3
2892+ */
2893+ public AttributeSet getCharacterAttribute(int i) {
2894+ View view = (View) AbstractButton.this.getClientProperty("html");
2895+ if (view != null) {
2896+ Document d = view.getDocument();
2897+ if (d instanceof StyledDocument) {
2898+ StyledDocument doc = (StyledDocument)d;
2899+ Element elem = doc.getCharacterElement(i);
2900+ if (elem != null) {
2901+ return elem.getAttributes();
2902+ }
2903+ }
2904+ }
2905+ return null;
2906+ }
2907+
2908+ /**
2909+ * Returns the start offset within the selected text.
2910+ * If there is no selection, but there is
2911+ * a caret, the start and end offsets will be the same.
2912+ *
2913+ * @return the index into the text of the start of the selection
2914+ * @since 1.3
2915+ */
2916+ public int getSelectionStart() {
2917+ // Text cannot be selected.
2918+ return -1;
2919+ }
2920+
2921+ /**
2922+ * Returns the end offset within the selected text.
2923+ * If there is no selection, but there is
2924+ * a caret, the start and end offsets will be the same.
2925+ *
2926+ * @return the index into teh text of the end of the selection
2927+ * @since 1.3
2928+ */
2929+ public int getSelectionEnd() {
2930+ // Text cannot be selected.
2931+ return -1;
2932+ }
2933+
2934+ /**
2935+ * Returns the portion of the text that is selected.
2936+ *
2937+ * @return the String portion of the text that is selected
2938+ * @since 1.3
2939+ */
2940+ public String getSelectedText() {
2941+ // Text cannot be selected.
2942+ return null;
2943+ }
2944+
2945+ /*
2946+ * Returns the text substring starting at the specified
2947+ * offset with the specified length.
2948+ */
2949+ private String getText(int offset, int length)
2950+ throws BadLocationException {
2951+
2952+ View view = (View) AbstractButton.this.getClientProperty("html");
2953+ if (view != null) {
2954+ Document d = view.getDocument();
2955+ if (d instanceof StyledDocument) {
2956+ StyledDocument doc = (StyledDocument)d;
2957+ return doc.getText(offset, length);
2958+ }
2959+ }
2960+ return null;
2961+ }
2962+
2963+ /*
2964+ * Returns the bounding rectangle for the component text.
2965+ */
2966+ private Rectangle getTextRectangle() {
2967+
2968+ String text = AbstractButton.this.getText();
2969+ Icon icon = (AbstractButton.this.isEnabled()) ? AbstractButton.this.getIcon() : AbstractButton.this.getDisabledIcon();
2970+
2971+ if ((icon == null) && (text == null)) {
2972+ return null;
2973+ }
2974+
2975+ Rectangle paintIconR = new Rectangle();
2976+ Rectangle paintTextR = new Rectangle();
2977+ Rectangle paintViewR = new Rectangle();
2978+ Insets paintViewInsets = new Insets(0, 0, 0, 0);
2979+
2980+ paintViewInsets = AbstractButton.this.getInsets(paintViewInsets);
2981+ paintViewR.x = paintViewInsets.left;
2982+ paintViewR.y = paintViewInsets.top;
2983+ paintViewR.width = AbstractButton.this.getWidth() - (paintViewInsets.left + paintViewInsets.right);
2984+ paintViewR.height = AbstractButton.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
2985+
2986+ String clippedText = SwingUtilities.layoutCompoundLabel(
2987+ AbstractButton.this,
2988+ getFontMetrics(getFont()),
2989+ text,
2990+ icon,
2991+ AbstractButton.this.getVerticalAlignment(),
2992+ AbstractButton.this.getHorizontalAlignment(),
2993+ AbstractButton.this.getVerticalTextPosition(),
2994+ AbstractButton.this.getHorizontalTextPosition(),
2995+ paintViewR,
2996+ paintIconR,
2997+ paintTextR,
2998+ 0);
2999+
3000+ return paintTextR;
3001+ }
3002+
3003+ // ----- AccessibleExtendedComponent
3004+
3005+ /**
3006+ * Returns the AccessibleExtendedComponent
3007+ *
3008+ * @return the AccessibleExtendedComponent
3009+ */
3010+ AccessibleExtendedComponent getAccessibleExtendedComponent() {
3011+ return this;
3012+ }
3013+
3014+ /**
3015+ * Returns the tool tip text
3016+ *
3017+ * @return the tool tip text, if supported, of the object;
3018+ * otherwise, null
3019+ * @since 1.4
3020+ */
3021+ public String getToolTipText() {
3022+ return AbstractButton.this.getToolTipText();
3023+ }
3024+
3025+ /**
3026+ * Returns the titled border text
3027+ *
3028+ * @return the titled border text, if supported, of the object;
3029+ * otherwise, null
3030+ * @since 1.4
3031+ */
3032+ public String getTitledBorderText() {
3033+ return super.getTitledBorderText();
3034+ }
3035+
3036+ /**
3037+ * Returns key bindings associated with this object
3038+ *
3039+ * @return the key bindings, if supported, of the object;
3040+ * otherwise, null
3041+ * @see AccessibleKeyBinding
3042+ * @since 1.4
3043+ */
3044+ public AccessibleKeyBinding getAccessibleKeyBinding() {
3045+ int mnemonic = AbstractButton.this.getMnemonic();
3046+ if (mnemonic == 0) {
3047+ return null;
3048+ }
3049+ return new ButtonKeyBinding(mnemonic);
3050+ }
3051+
3052+ class ButtonKeyBinding implements AccessibleKeyBinding {
3053+ int mnemonic;
3054+
3055+ ButtonKeyBinding(int mnemonic) {
3056+ this.mnemonic = mnemonic;
3057+ }
3058+
3059+ /**
3060+ * Returns the number of key bindings for this object
3061+ *
3062+ * @return the zero-based number of key bindings for this object
3063+ */
3064+ public int getAccessibleKeyBindingCount() {
3065+ return 1;
3066+ }
3067+
3068+ /**
3069+ * Returns a key binding for this object. The value returned is an
3070+ * java.lang.Object which must be cast to appropriate type depending
3071+ * on the underlying implementation of the key. For example, if the
3072+ * Object returned is a javax.swing.KeyStroke, the user of this
3073+ * method should do the following:
3074+ * <nf><code>
3075+ * Component c = <get the component that has the key bindings>
3076+ * AccessibleContext ac = c.getAccessibleContext();
3077+ * AccessibleKeyBinding akb = ac.getAccessibleKeyBinding();
3078+ * for (int i = 0; i < akb.getAccessibleKeyBindingCount(); i++) {
3079+ * Object o = akb.getAccessibleKeyBinding(i);
3080+ * if (o instanceof javax.swing.KeyStroke) {
3081+ * javax.swing.KeyStroke keyStroke = (javax.swing.KeyStroke)o;
3082+ * <do something with the key binding>
3083+ * }
3084+ * }
3085+ * </code></nf>
3086+ *
3087+ * @param i zero-based index of the key bindings
3088+ * @return a javax.lang.Object which specifies the key binding
3089+ * @exception IllegalArgumentException if the index is
3090+ * out of bounds
3091+ * @see #getAccessibleKeyBindingCount
3092+ */
3093+ public java.lang.Object getAccessibleKeyBinding(int i) {
3094+ if (i != 0) {
3095+ throw new IllegalArgumentException();
3096+ }
3097+ return KeyStroke.getKeyStroke(mnemonic, 0);
3098+ }
3099+ }
3100+ }
3101+}