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