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