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