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