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