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