001 /* AbstractButton.java -- Provides basic button functionality. 002 Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 package javax.swing; 039 040 import java.awt.Component; 041 import java.awt.Graphics; 042 import java.awt.Image; 043 import java.awt.Insets; 044 import java.awt.ItemSelectable; 045 import java.awt.LayoutManager; 046 import java.awt.Point; 047 import java.awt.Rectangle; 048 import java.awt.Shape; 049 import java.awt.event.ActionEvent; 050 import java.awt.event.ActionListener; 051 import java.awt.event.ItemEvent; 052 import java.awt.event.ItemListener; 053 import java.awt.image.ImageObserver; 054 import java.beans.PropertyChangeEvent; 055 import java.beans.PropertyChangeListener; 056 import java.io.Serializable; 057 import java.util.Enumeration; 058 059 import javax.accessibility.Accessible; 060 import javax.accessibility.AccessibleAction; 061 import javax.accessibility.AccessibleContext; 062 import javax.accessibility.AccessibleIcon; 063 import javax.accessibility.AccessibleRelation; 064 import javax.accessibility.AccessibleRelationSet; 065 import javax.accessibility.AccessibleState; 066 import javax.accessibility.AccessibleStateSet; 067 import javax.accessibility.AccessibleText; 068 import javax.accessibility.AccessibleValue; 069 import javax.swing.event.ChangeEvent; 070 import javax.swing.event.ChangeListener; 071 import javax.swing.plaf.ButtonUI; 072 import javax.swing.plaf.basic.BasicHTML; 073 import javax.swing.text.AttributeSet; 074 import javax.swing.text.BadLocationException; 075 import javax.swing.text.Document; 076 import javax.swing.text.Element; 077 import javax.swing.text.Position; 078 import javax.swing.text.StyledDocument; 079 import javax.swing.text.View; 080 081 082 /** 083 * Provides an abstract implementation of common button behaviour, 084 * data model and look & feel. 085 * 086 * <p>This class is supposed to serve as a base class for 087 * several kinds of buttons with similar but non-identical semantics: 088 * toggle buttons (radio buttons and checkboxes), simple push buttons, 089 * menu items, etc.</p> 090 * 091 * <p>Buttons have many properties, some of which are stored in this class 092 * while others are delegated to the button's model. The following properties 093 * are available:</p> 094 * 095 * <table> 096 * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr> 097 * 098 * <tr><td>action </td><td>button</td> <td>no</td></tr> 099 * <tr><td>actionCommand </td><td>model</td> <td>no</td></tr> 100 * <tr><td>borderPainted </td><td>button</td> <td>yes</td></tr> 101 * <tr><td>contentAreaFilled </td><td>button</td> <td>yes</td></tr> 102 * <tr><td>disabledIcon </td><td>button</td> <td>yes</td></tr> 103 * <tr><td>disabledSelectedIcon </td><td>button</td> <td>yes</td></tr> 104 * <tr><td>displayedMnemonicIndex </td><td>button</td> <td>no</td></tr> 105 * <tr><td>enabled </td><td>model</td> <td>no</td></tr> 106 * <tr><td>focusPainted </td><td>button</td> <td>yes</td></tr> 107 * <tr><td>horizontalAlignment </td><td>button</td> <td>yes</td></tr> 108 * <tr><td>horizontalTextPosition </td><td>button</td> <td>yes</td></tr> 109 * <tr><td>icon </td><td>button</td> <td>yes</td></tr> 110 * <tr><td>iconTextGap </td><td>button</td> <td>no</td></tr> 111 * <tr><td>label (same as text) </td><td>model</td> <td>yes</td></tr> 112 * <tr><td>margin </td><td>button</td> <td>yes</td></tr> 113 * <tr><td>multiClickThreshold </td><td>button</td> <td>no</td></tr> 114 * <tr><td>pressedIcon </td><td>button</td> <td>yes</td></tr> 115 * <tr><td>rolloverEnabled </td><td>button</td> <td>yes</td></tr> 116 * <tr><td>rolloverIcon </td><td>button</td> <td>yes</td></tr> 117 * <tr><td>rolloverSelectedIcon </td><td>button</td> <td>yes</td></tr> 118 * <tr><td>selected </td><td>model</td> <td>no</td></tr> 119 * <tr><td>selectedIcon </td><td>button</td> <td>yes</td></tr> 120 * <tr><td>selectedObjects </td><td>button</td> <td>no</td></tr> 121 * <tr><td>text </td><td>model</td> <td>yes</td></tr> 122 * <tr><td>UI </td><td>button</td> <td>yes</td></tr> 123 * <tr><td>verticalAlignment </td><td>button</td> <td>yes</td></tr> 124 * <tr><td>verticalTextPosition </td><td>button</td> <td>yes</td></tr> 125 * 126 * </table> 127 * 128 * <p>The various behavioral aspects of these properties follows:</p> 129 * 130 * <ul> 131 * 132 * <li>When non-bound properties stored in the button change, the button 133 * fires ChangeEvents to its ChangeListeners.</li> 134 * 135 * <li>When bound properties stored in the button change, the button fires 136 * PropertyChangeEvents to its PropertyChangeListeners</li> 137 * 138 * <li>If any of the model's properties change, it fires a ChangeEvent to 139 * its ChangeListeners, which include the button.</li> 140 * 141 * <li>If the button receives a ChangeEvent from its model, it will 142 * propagate the ChangeEvent to its ChangeListeners, with the ChangeEvent's 143 * "source" property set to refer to the button, rather than the model. The 144 * the button will request a repaint, to paint its updated state.</li> 145 * 146 * <li>If the model's "selected" property changes, the model will fire an 147 * ItemEvent to its ItemListeners, which include the button, in addition to 148 * the ChangeEvent which models the property change. The button propagates 149 * ItemEvents directly to its ItemListeners.</li> 150 * 151 * <li>If the model's armed and pressed properties are simultaneously 152 * <code>true</code>, the model will fire an ActionEvent to its 153 * ActionListeners, which include the button. The button will propagate 154 * this ActionEvent to its ActionListeners, with the ActionEvent's "source" 155 * property set to refer to the button, rather than the model.</li> 156 * 157 * </ul> 158 * 159 * @author Ronald Veldema (rveldema@cs.vu.nl) 160 * @author Graydon Hoare (graydon@redhat.com) 161 */ 162 163 public abstract class AbstractButton extends JComponent 164 implements ItemSelectable, SwingConstants 165 { 166 private static final long serialVersionUID = -937921345538462020L; 167 168 /** 169 * An extension of ChangeListener to be serializable. 170 */ 171 protected class ButtonChangeListener 172 implements ChangeListener, Serializable 173 { 174 private static final long serialVersionUID = 1471056094226600578L; 175 176 /** 177 * The spec has no public/protected constructor for this class, so do we. 178 */ 179 ButtonChangeListener() 180 { 181 // Nothing to do here. 182 } 183 184 /** 185 * Notified when the target of the listener changes its state. 186 * 187 * @param ev the ChangeEvent describing the change 188 */ 189 public void stateChanged(ChangeEvent ev) 190 { 191 getEventHandler().stateChanged(ev); 192 } 193 } 194 195 /** 196 * The combined event handler for ActionEvent, ChangeEvent and 197 * ItemEvent. This combines ButtonChangeListener, ActionListener 198 */ 199 private class EventHandler 200 implements ActionListener, ChangeListener, ItemListener 201 { 202 public void actionPerformed(ActionEvent ev) 203 { 204 fireActionPerformed(ev); 205 } 206 207 public void stateChanged(ChangeEvent ev) 208 { 209 fireStateChanged(); 210 repaint(); 211 } 212 213 public void itemStateChanged(ItemEvent ev) 214 { 215 fireItemStateChanged(ev); 216 } 217 } 218 219 /** The icon displayed by default. */ 220 Icon default_icon; 221 222 /** The icon displayed when the button is pressed. */ 223 Icon pressed_icon; 224 225 /** The icon displayed when the button is disabled. */ 226 Icon disabledIcon; 227 228 /** The icon displayed when the button is selected. */ 229 Icon selectedIcon; 230 231 /** The icon displayed when the button is selected but disabled. */ 232 Icon disabledSelectedIcon; 233 234 /** The icon displayed when the button is rolled over. */ 235 Icon rolloverIcon; 236 237 /** The icon displayed when the button is selected and rolled over. */ 238 Icon rolloverSelectedIcon; 239 240 /** The icon currently displayed. */ 241 Icon current_icon; 242 243 /** The text displayed in the button. */ 244 String text; 245 246 /** 247 * The gap between icon and text, if both icon and text are 248 * non-<code>null</code>. 249 */ 250 int iconTextGap; 251 252 /** The vertical alignment of the button's text and icon. */ 253 int verticalAlignment; 254 255 /** The horizontal alignment of the button's text and icon. */ 256 int horizontalAlignment; 257 258 /** The horizontal position of the button's text relative to its icon. */ 259 int horizontalTextPosition; 260 261 /** The vertical position of the button's text relative to its icon. */ 262 int verticalTextPosition; 263 264 /** Whether or not the button paints its border. */ 265 boolean borderPainted; 266 267 /** Whether or not the button paints its focus state. */ 268 boolean focusPainted; 269 270 /** Whether or not the button fills its content area. */ 271 boolean contentAreaFilled; 272 273 /** Whether rollover is enabled. */ 274 boolean rollOverEnabled; 275 276 /** The action taken when the button is clicked. */ 277 Action action; 278 279 /** The button's current state. */ 280 protected ButtonModel model; 281 282 /** The margin between the button's border and its label. */ 283 Insets margin; 284 285 /** 286 * A hint to the look and feel class, suggesting which character in the 287 * button's label should be underlined when drawing the label. 288 */ 289 int mnemonicIndex; 290 291 /** 292 * Listener the button uses to receive ActionEvents from its model. 293 */ 294 protected ActionListener actionListener; 295 296 /** 297 * Listener the button uses to receive ItemEvents from its model. 298 */ 299 protected ItemListener itemListener; 300 301 /** 302 * Listener the button uses to receive ChangeEvents from its model. 303 */ 304 protected ChangeListener changeListener; 305 306 /** 307 * The event handler for ActionEvent, ItemEvent and ChangeEvent. 308 * This replaces the above three handlers and combines them 309 * into one for efficiency. 310 */ 311 private EventHandler eventHandler; 312 313 /** 314 * The time in milliseconds in which clicks get coalesced into a single 315 * <code>ActionEvent</code>. 316 */ 317 long multiClickThreshhold; 318 319 /** 320 * Listener the button uses to receive PropertyChangeEvents from its 321 * Action. 322 */ 323 PropertyChangeListener actionPropertyChangeListener; 324 325 /** ChangeEvent that is fired to button's ChangeEventListeners */ 326 protected ChangeEvent changeEvent = new ChangeEvent(this); 327 328 /** 329 * Indicates if the borderPainted property has been set by a client 330 * program or by the UI. 331 * 332 * @see #setUIProperty(String, Object) 333 * @see LookAndFeel#installProperty(JComponent, String, Object) 334 */ 335 private boolean clientBorderPaintedSet = false; 336 337 /** 338 * Indicates if the rolloverEnabled property has been set by a client 339 * program or by the UI. 340 * 341 * @see #setUIProperty(String, Object) 342 * @see LookAndFeel#installProperty(JComponent, String, Object) 343 */ 344 private boolean clientRolloverEnabledSet = false; 345 346 /** 347 * Indicates if the iconTextGap property has been set by a client 348 * program or by the UI. 349 * 350 * @see #setUIProperty(String, Object) 351 * @see LookAndFeel#installProperty(JComponent, String, Object) 352 */ 353 private boolean clientIconTextGapSet = false; 354 355 /** 356 * Indicates if the contentAreaFilled property has been set by a client 357 * program or by the UI. 358 * 359 * @see #setUIProperty(String, Object) 360 * @see LookAndFeel#installProperty(JComponent, String, Object) 361 */ 362 private boolean clientContentAreaFilledSet = false; 363 364 /** 365 * Fired in a PropertyChangeEvent when the "borderPainted" property changes. 366 */ 367 public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted"; 368 369 /** 370 * Fired in a PropertyChangeEvent when the "contentAreaFilled" property 371 * changes. 372 */ 373 public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = 374 "contentAreaFilled"; 375 376 /** 377 * Fired in a PropertyChangeEvent when the "disabledIcon" property changes. 378 */ 379 public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon"; 380 381 /** 382 * Fired in a PropertyChangeEvent when the "disabledSelectedIcon" property 383 * changes. 384 */ 385 public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = 386 "disabledSelectedIcon"; 387 388 /** 389 * Fired in a PropertyChangeEvent when the "focusPainted" property changes. 390 */ 391 public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted"; 392 393 /** 394 * Fired in a PropertyChangeEvent when the "horizontalAlignment" property 395 * changes. 396 */ 397 public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = 398 "horizontalAlignment"; 399 400 /** 401 * Fired in a PropertyChangeEvent when the "horizontalTextPosition" property 402 * changes. 403 */ 404 public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = 405 "horizontalTextPosition"; 406 407 /** 408 * Fired in a PropertyChangeEvent when the "icon" property changes. */ 409 public static final String ICON_CHANGED_PROPERTY = "icon"; 410 411 /** Fired in a PropertyChangeEvent when the "margin" property changes. */ 412 public static final String MARGIN_CHANGED_PROPERTY = "margin"; 413 414 /** Fired in a PropertyChangeEvent when the "mnemonic" property changes. */ 415 public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic"; 416 417 /** Fired in a PropertyChangeEvent when the "model" property changes. */ 418 public static final String MODEL_CHANGED_PROPERTY = "model"; 419 420 /** Fired in a PropertyChangeEvent when the "pressedIcon" property changes. */ 421 public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon"; 422 423 /** 424 * Fired in a PropertyChangeEvent when the "rolloverEnabled" property 425 * changes. 426 */ 427 public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = 428 "rolloverEnabled"; 429 430 /** 431 * Fired in a PropertyChangeEvent when the "rolloverIcon" property changes. 432 */ 433 public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon"; 434 435 /** 436 * Fired in a PropertyChangeEvent when the "rolloverSelectedIcon" property 437 * changes. 438 */ 439 public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = 440 "rolloverSelectedIcon"; 441 442 /** 443 * Fired in a PropertyChangeEvent when the "selectedIcon" property changes. 444 */ 445 public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon"; 446 447 /** Fired in a PropertyChangeEvent when the "text" property changes. */ 448 public static final String TEXT_CHANGED_PROPERTY = "text"; 449 450 /** 451 * Fired in a PropertyChangeEvent when the "verticalAlignment" property 452 * changes. 453 */ 454 public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = 455 "verticalAlignment"; 456 457 /** 458 * Fired in a PropertyChangeEvent when the "verticalTextPosition" property 459 * changes. 460 */ 461 public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = 462 "verticalTextPosition"; 463 464 /** 465 * A Java Accessibility extension of the AbstractButton. 466 */ 467 protected abstract class AccessibleAbstractButton 468 extends AccessibleJComponent implements AccessibleAction, AccessibleValue, 469 AccessibleText 470 { 471 private static final long serialVersionUID = -5673062525319836790L; 472 473 protected AccessibleAbstractButton() 474 { 475 // Nothing to do here yet. 476 } 477 478 /** 479 * Returns the accessible state set of this object. In addition to the 480 * superclass's states, the <code>AccessibleAbstractButton</code> 481 * supports the following states: {@link AccessibleState#ARMED}, 482 * {@link AccessibleState#FOCUSED}, {@link AccessibleState#PRESSED} and 483 * {@link AccessibleState#CHECKED}. 484 * 485 * @return the current state of this accessible object 486 */ 487 public AccessibleStateSet getAccessibleStateSet() 488 { 489 AccessibleStateSet state = super.getAccessibleStateSet(); 490 491 if (getModel().isArmed()) 492 state.add(AccessibleState.ARMED); 493 if (getModel().isPressed()) 494 state.add(AccessibleState.PRESSED); 495 if (isSelected()) 496 state.add(AccessibleState.CHECKED); 497 498 return state; 499 } 500 501 /** 502 * Returns the accessible name for the button. 503 */ 504 public String getAccessibleName() 505 { 506 String result = super.getAccessibleName(); 507 if (result == null) 508 result = text; 509 return result; 510 } 511 512 /** 513 * Returns the accessible icons of this object. If the AbstractButton's 514 * icon is an Accessible, and it's AccessibleContext is an AccessibleIcon, 515 * then this AccessibleIcon is returned, otherwise <code>null</code>. 516 * 517 * @return the accessible icons of this object, or <code>null</code> if 518 * there is no accessible icon 519 */ 520 public AccessibleIcon[] getAccessibleIcon() 521 { 522 AccessibleIcon[] ret = null; 523 Icon icon = getIcon(); 524 if (icon instanceof Accessible) 525 { 526 AccessibleContext ctx = ((Accessible) icon).getAccessibleContext(); 527 if (ctx instanceof AccessibleIcon) 528 { 529 ret = new AccessibleIcon[]{ (AccessibleIcon) ctx }; 530 } 531 } 532 return ret; 533 } 534 535 /** 536 * Returns the accessible relations of this AccessibleAbstractButton. 537 * If the AbstractButton is part of a ButtonGroup, then all the buttons 538 * in this button group are added as targets in a MEMBER_OF relation, 539 * otherwise an empty relation set is returned (from super). 540 * 541 * @return the accessible relations of this AccessibleAbstractButton 542 */ 543 public AccessibleRelationSet getAccessibleRelationSet() 544 { 545 AccessibleRelationSet relations = super.getAccessibleRelationSet(); 546 ButtonModel model = getModel(); 547 if (model instanceof DefaultButtonModel) 548 { 549 ButtonGroup group = ((DefaultButtonModel) model).getGroup(); 550 if (group != null) 551 { 552 Object[] target = new Object[group.getButtonCount()]; 553 Enumeration els = group.getElements(); 554 555 for (int index = 0; els.hasMoreElements(); ++index) 556 { 557 target[index] = els.nextElement(); 558 } 559 560 AccessibleRelation rel = 561 new AccessibleRelation(AccessibleRelation.MEMBER_OF); 562 rel.setTarget(target); 563 relations.add(rel); 564 } 565 } 566 return relations; 567 } 568 569 /** 570 * Returns the accessible action associated with this object. For buttons, 571 * this will be <code>this</code>. 572 * 573 * @return <code>this</code> 574 */ 575 public AccessibleAction getAccessibleAction() 576 { 577 return this; 578 } 579 580 /** 581 * Returns the accessible value of this AccessibleAbstractButton, which 582 * is always <code>this</code>. 583 * 584 * @return the accessible value of this AccessibleAbstractButton, which 585 * is always <code>this</code> 586 */ 587 public AccessibleValue getAccessibleValue() 588 { 589 return this; 590 } 591 592 /** 593 * Returns the number of accessible actions that are supported by this 594 * object. Buttons support one action by default ('press button'), so this 595 * method always returns <code>1</code>. 596 * 597 * @return <code>1</code>, the number of supported accessible actions 598 */ 599 public int getAccessibleActionCount() 600 { 601 return 1; 602 } 603 604 /** 605 * Returns a description for the action with the specified index or 606 * <code>null</code> if such action does not exist. 607 * 608 * @param actionIndex the zero based index to the actions 609 * 610 * @return a description for the action with the specified index or 611 * <code>null</code> if such action does not exist 612 */ 613 public String getAccessibleActionDescription(int actionIndex) 614 { 615 String descr = null; 616 if (actionIndex == 0) 617 { 618 // FIXME: Supply localized descriptions in the UIDefaults. 619 descr = UIManager.getString("AbstractButton.clickText"); 620 } 621 return descr; 622 } 623 624 /** 625 * Performs the acccessible action with the specified index on this object. 626 * Since buttons have only one action by default (which is to press the 627 * button), this method performs a 'press button' when the specified index 628 * is <code>0</code> and nothing otherwise. 629 * 630 * @param actionIndex a zero based index into the actions of this button 631 * 632 * @return <code>true</code> if the specified action has been performed 633 * successfully, <code>false</code> otherwise 634 */ 635 public boolean doAccessibleAction(int actionIndex) 636 { 637 boolean retVal = false; 638 if (actionIndex == 0) 639 { 640 doClick(); 641 retVal = true; 642 } 643 return retVal; 644 } 645 646 /** 647 * Returns the current value of this object as a number. This 648 * implementation returns an <code>Integer(1)</code> if the button is 649 * selected, <code>Integer(0)</code> if the button is not selected. 650 * 651 * @return the current value of this object as a number 652 */ 653 public Number getCurrentAccessibleValue() 654 { 655 Integer retVal; 656 if (isSelected()) 657 retVal = new Integer(1); 658 else 659 retVal = new Integer(0); 660 return retVal; 661 } 662 663 /** 664 * Sets the current accessible value as object. If the specified number 665 * is 0 the button will be deselected, otherwise the button will 666 * be selected. 667 * 668 * @param value 0 for deselected button, other for selected button 669 * 670 * @return <code>true</code> if the value has been set, <code>false</code> 671 * otherwise 672 */ 673 public boolean setCurrentAccessibleValue(Number value) 674 { 675 boolean retVal = false; 676 if (value != null) 677 { 678 if (value.intValue() == 0) 679 setSelected(false); 680 else 681 setSelected(true); 682 retVal = true; 683 } 684 return retVal; 685 } 686 687 /** 688 * Returns the minimum accessible value for the AccessibleAbstractButton, 689 * which is <code>0</code>. 690 * 691 * @return the minimimum accessible value for the AccessibleAbstractButton, 692 * which is <code>0</code> 693 */ 694 public Number getMinimumAccessibleValue() 695 { 696 return new Integer(0); 697 } 698 699 /** 700 * Returns the maximum accessible value for the AccessibleAbstractButton, 701 * which is <code>1</code>. 702 * 703 * @return the maximum accessible value for the AccessibleAbstractButton, 704 * which is <code>1</code> 705 */ 706 public Number getMaximumAccessibleValue() 707 { 708 return new Integer(1); 709 } 710 711 /** 712 * Returns the accessible text for this AccessibleAbstractButton. This 713 * will be <code>null</code> if the button has a non-HTML label, otherwise 714 * <code>this</code>. 715 * 716 * @return the accessible text for this AccessibleAbstractButton 717 */ 718 public AccessibleText getAccessibleText() 719 { 720 AccessibleText accessibleText = null; 721 if (getClientProperty(BasicHTML.propertyKey) != null) 722 accessibleText = this; 723 724 return accessibleText; 725 } 726 727 /** 728 * Returns the index of the label's character at the specified point, 729 * relative to the local bounds of the button. This only works for 730 * HTML labels. 731 * 732 * @param p the point, relative to the buttons local bounds 733 * 734 * @return the index of the label's character at the specified point 735 */ 736 public int getIndexAtPoint(Point p) 737 { 738 int index = -1; 739 View view = (View) getClientProperty(BasicHTML.propertyKey); 740 if (view != null) 741 { 742 Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight()); 743 index = view.viewToModel(p.x, p.y, shape, new Position.Bias[1]); 744 } 745 return index; 746 } 747 748 /** 749 * Returns the bounds of the character at the specified index of the 750 * button's label. This will only work for HTML labels. 751 * 752 * @param i the index of the character of the label 753 * 754 * @return the bounds of the character at the specified index of the 755 * button's label 756 */ 757 public Rectangle getCharacterBounds(int i) 758 { 759 Rectangle rect = null; 760 View view = (View) getClientProperty(BasicHTML.propertyKey); 761 if (view != null) 762 { 763 Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight()); 764 try 765 { 766 Shape s = view.modelToView(i, shape, Position.Bias.Forward); 767 rect = s.getBounds(); 768 } 769 catch (BadLocationException ex) 770 { 771 rect = null; 772 } 773 } 774 return rect; 775 } 776 777 /** 778 * Returns the number of characters in the button's label. 779 * 780 * @return the bounds of the character at the specified index of the 781 * button's label 782 */ 783 public int getCharCount() 784 { 785 int charCount; 786 View view = (View) getClientProperty(BasicHTML.propertyKey); 787 if (view != null) 788 { 789 charCount = view.getDocument().getLength(); 790 } 791 else 792 { 793 charCount = getAccessibleName().length(); 794 } 795 return charCount; 796 } 797 798 /** 799 * This always returns <code>-1</code> since there is no caret in a button. 800 * 801 * @return <code>-1</code> since there is no caret in a button 802 */ 803 public int getCaretPosition() 804 { 805 return -1; 806 } 807 808 /** 809 * Returns the character, word or sentence at the specified index. The 810 * <code>part</code> parameter determines what is returned, the character, 811 * word or sentence after the index. 812 * 813 * @param part one of {@link AccessibleText#CHARACTER}, 814 * {@link AccessibleText#WORD} or 815 * {@link AccessibleText#SENTENCE}, specifying what is returned 816 * @param index the index 817 * 818 * @return the character, word or sentence after <code>index</code> 819 */ 820 public String getAtIndex(int part, int index) 821 { 822 String result = ""; 823 int startIndex = -1; 824 int endIndex = -1; 825 switch(part) 826 { 827 case AccessibleText.CHARACTER: 828 result = String.valueOf(text.charAt(index)); 829 break; 830 case AccessibleText.WORD: 831 startIndex = text.lastIndexOf(' ', index); 832 endIndex = text.indexOf(' ', startIndex + 1); 833 if (endIndex == -1) 834 endIndex = startIndex + 1; 835 result = text.substring(startIndex + 1, endIndex); 836 break; 837 case AccessibleText.SENTENCE: 838 default: 839 startIndex = text.lastIndexOf('.', index); 840 endIndex = text.indexOf('.', startIndex + 1); 841 if (endIndex == -1) 842 endIndex = startIndex + 1; 843 result = text.substring(startIndex + 1, endIndex); 844 break; 845 } 846 return result; 847 } 848 849 /** 850 * Returns the character, word or sentence after the specified index. The 851 * <code>part</code> parameter determines what is returned, the character, 852 * word or sentence after the index. 853 * 854 * @param part one of {@link AccessibleText#CHARACTER}, 855 * {@link AccessibleText#WORD} or 856 * {@link AccessibleText#SENTENCE}, specifying what is returned 857 * @param index the index 858 * 859 * @return the character, word or sentence after <code>index</code> 860 */ 861 public String getAfterIndex(int part, int index) 862 { 863 String result = ""; 864 int startIndex = -1; 865 int endIndex = -1; 866 switch(part) 867 { 868 case AccessibleText.CHARACTER: 869 result = String.valueOf(text.charAt(index + 1)); 870 break; 871 case AccessibleText.WORD: 872 startIndex = text.indexOf(' ', index); 873 endIndex = text.indexOf(' ', startIndex + 1); 874 if (endIndex == -1) 875 endIndex = startIndex + 1; 876 result = text.substring(startIndex + 1, endIndex); 877 break; 878 case AccessibleText.SENTENCE: 879 default: 880 startIndex = text.indexOf('.', index); 881 endIndex = text.indexOf('.', startIndex + 1); 882 if (endIndex == -1) 883 endIndex = startIndex + 1; 884 result = text.substring(startIndex + 1, endIndex); 885 break; 886 } 887 return result; 888 } 889 890 /** 891 * Returns the character, word or sentence before the specified index. The 892 * <code>part</code> parameter determines what is returned, the character, 893 * word or sentence before the index. 894 * 895 * @param part one of {@link AccessibleText#CHARACTER}, 896 * {@link AccessibleText#WORD} or 897 * {@link AccessibleText#SENTENCE}, specifying what is returned 898 * @param index the index 899 * 900 * @return the character, word or sentence before <code>index</code> 901 */ 902 public String getBeforeIndex(int part, int index) 903 { 904 String result = ""; 905 int startIndex = -1; 906 int endIndex = -1; 907 switch(part) 908 { 909 case AccessibleText.CHARACTER: 910 result = String.valueOf(text.charAt(index - 1)); 911 break; 912 case AccessibleText.WORD: 913 endIndex = text.lastIndexOf(' ', index); 914 if (endIndex == -1) 915 endIndex = 0; 916 startIndex = text.lastIndexOf(' ', endIndex - 1); 917 result = text.substring(startIndex + 1, endIndex); 918 break; 919 case AccessibleText.SENTENCE: 920 default: 921 endIndex = text.lastIndexOf('.', index); 922 if (endIndex == -1) 923 endIndex = 0; 924 startIndex = text.lastIndexOf('.', endIndex - 1); 925 result = text.substring(startIndex + 1, endIndex); 926 break; 927 } 928 return result; 929 } 930 931 /** 932 * Returns the text attribute for the character at the specified character 933 * index. 934 * 935 * @param i the character index 936 * 937 * @return the character attributes for the specified character or 938 * <code>null</code> if the character has no attributes 939 */ 940 public AttributeSet getCharacterAttribute(int i) 941 { 942 AttributeSet atts = null; 943 View view = (View) getClientProperty(BasicHTML.propertyKey); 944 if (view != null) 945 { 946 Document doc = view.getDocument(); 947 if (doc instanceof StyledDocument) 948 { 949 StyledDocument sDoc = (StyledDocument) doc; 950 Element charEl = sDoc.getCharacterElement(i); 951 if (charEl != null) 952 atts = charEl.getAttributes(); 953 } 954 } 955 return atts; 956 } 957 958 /** 959 * This always returns <code>-1</code> since 960 * button labels can't be selected. 961 * 962 * @return <code>-1</code>, button labels can't be selected 963 */ 964 public int getSelectionStart() 965 { 966 return -1; 967 } 968 969 /** 970 * This always returns <code>-1</code> since 971 * button labels can't be selected. 972 * 973 * @return <code>-1</code>, button labels can't be selected 974 */ 975 public int getSelectionEnd() 976 { 977 return -1; 978 } 979 980 /** 981 * Returns the selected text. This always returns <code>null</code> since 982 * button labels can't be selected. 983 * 984 * @return <code>null</code>, button labels can't be selected 985 */ 986 public String getSelectedText() 987 { 988 return null; 989 } 990 } 991 992 /** 993 * Creates a new AbstractButton object. Subclasses should call the following 994 * sequence in their constructor in order to initialize the button correctly: 995 * <pre> 996 * super(); 997 * init(text, icon); 998 * </pre> 999 * 1000 * The {@link #init(String, Icon)} method is not called automatically by this 1001 * constructor. 1002 * 1003 * @see #init(String, Icon) 1004 */ 1005 public AbstractButton() 1006 { 1007 horizontalAlignment = CENTER; 1008 horizontalTextPosition = TRAILING; 1009 verticalAlignment = CENTER; 1010 verticalTextPosition = CENTER; 1011 borderPainted = true; 1012 contentAreaFilled = true; 1013 focusPainted = true; 1014 setFocusable(true); 1015 setAlignmentX(CENTER_ALIGNMENT); 1016 setAlignmentY(CENTER_ALIGNMENT); 1017 setDisplayedMnemonicIndex(-1); 1018 setOpaque(true); 1019 text = ""; 1020 // testing on JRE1.5 shows that the iconTextGap default value is 1021 // hard-coded here and the 'Button.iconTextGap' setting in the 1022 // UI defaults is ignored, at least by the MetalLookAndFeel 1023 iconTextGap = 4; 1024 } 1025 1026 /** 1027 * Get the model the button is currently using. 1028 * 1029 * @return The current model 1030 */ 1031 public ButtonModel getModel() 1032 { 1033 return model; 1034 } 1035 1036 /** 1037 * Set the model the button is currently using. This un-registers all 1038 * listeners associated with the current model, and re-registers them 1039 * with the new model. 1040 * 1041 * @param newModel The new model 1042 */ 1043 public void setModel(ButtonModel newModel) 1044 { 1045 if (newModel == model) 1046 return; 1047 1048 if (model != null) 1049 { 1050 model.removeActionListener(actionListener); 1051 actionListener = null; 1052 model.removeChangeListener(changeListener); 1053 changeListener = null; 1054 model.removeItemListener(itemListener); 1055 itemListener = null; 1056 } 1057 ButtonModel old = model; 1058 model = newModel; 1059 if (model != null) 1060 { 1061 actionListener = createActionListener(); 1062 model.addActionListener(actionListener); 1063 changeListener = createChangeListener(); 1064 model.addChangeListener(changeListener); 1065 itemListener = createItemListener(); 1066 model.addItemListener(itemListener); 1067 } 1068 firePropertyChange(MODEL_CHANGED_PROPERTY, old, model); 1069 revalidate(); 1070 repaint(); 1071 } 1072 1073 protected void init(String text, Icon icon) 1074 { 1075 // If text is null, we fall back to the empty 1076 // string (which is set using AbstractButton's 1077 // constructor). 1078 // This way the behavior of the JDK is matched. 1079 if(text != null) 1080 setText(text); 1081 1082 if (icon != null) 1083 default_icon = icon; 1084 1085 updateUI(); 1086 } 1087 1088 /** 1089 * <p>Returns the action command string for this button's model.</p> 1090 * 1091 * <p>If the action command was set to <code>null</code>, the button's 1092 * text (label) is returned instead.</p> 1093 * 1094 * @return The current action command string from the button's model 1095 */ 1096 public String getActionCommand() 1097 { 1098 String ac = model.getActionCommand(); 1099 if (ac != null) 1100 return ac; 1101 else 1102 return text; 1103 } 1104 1105 /** 1106 * Sets the action command string for this button's model. 1107 * 1108 * @param actionCommand The new action command string to set in the button's 1109 * model. 1110 */ 1111 public void setActionCommand(String actionCommand) 1112 { 1113 if (model != null) 1114 model.setActionCommand(actionCommand); 1115 } 1116 1117 /** 1118 * Adds an ActionListener to the button's listener list. When the 1119 * button's model is clicked it fires an ActionEvent, and these 1120 * listeners will be called. 1121 * 1122 * @param l The new listener to add 1123 */ 1124 public void addActionListener(ActionListener l) 1125 { 1126 listenerList.add(ActionListener.class, l); 1127 } 1128 1129 /** 1130 * Removes an ActionListener from the button's listener list. 1131 * 1132 * @param l The listener to remove 1133 */ 1134 public void removeActionListener(ActionListener l) 1135 { 1136 listenerList.remove(ActionListener.class, l); 1137 } 1138 1139 /** 1140 * Returns all added <code>ActionListener</code> objects. 1141 * 1142 * @return an array of listeners 1143 * 1144 * @since 1.4 1145 */ 1146 public ActionListener[] getActionListeners() 1147 { 1148 return (ActionListener[]) listenerList.getListeners(ActionListener.class); 1149 } 1150 1151 /** 1152 * Adds an ItemListener to the button's listener list. When the button's 1153 * model changes state (between any of ARMED, ENABLED, PRESSED, ROLLOVER 1154 * or SELECTED) it fires an ItemEvent, and these listeners will be 1155 * called. 1156 * 1157 * @param l The new listener to add 1158 */ 1159 public void addItemListener(ItemListener l) 1160 { 1161 listenerList.add(ItemListener.class, l); 1162 } 1163 1164 /** 1165 * Removes an ItemListener from the button's listener list. 1166 * 1167 * @param l The listener to remove 1168 */ 1169 public void removeItemListener(ItemListener l) 1170 { 1171 listenerList.remove(ItemListener.class, l); 1172 } 1173 1174 /** 1175 * Returns all added <code>ItemListener</code> objects. 1176 * 1177 * @return an array of listeners 1178 * 1179 * @since 1.4 1180 */ 1181 public ItemListener[] getItemListeners() 1182 { 1183 return (ItemListener[]) listenerList.getListeners(ItemListener.class); 1184 } 1185 1186 /** 1187 * Adds a ChangeListener to the button's listener list. When the button's 1188 * model changes any of its (non-bound) properties, these listeners will be 1189 * called. 1190 * 1191 * @param l The new listener to add 1192 */ 1193 public void addChangeListener(ChangeListener l) 1194 { 1195 listenerList.add(ChangeListener.class, l); 1196 } 1197 1198 /** 1199 * Removes a ChangeListener from the button's listener list. 1200 * 1201 * @param l The listener to remove 1202 */ 1203 public void removeChangeListener(ChangeListener l) 1204 { 1205 listenerList.remove(ChangeListener.class, l); 1206 } 1207 1208 /** 1209 * Returns all added <code>ChangeListener</code> objects. 1210 * 1211 * @return an array of listeners 1212 * 1213 * @since 1.4 1214 */ 1215 public ChangeListener[] getChangeListeners() 1216 { 1217 return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); 1218 } 1219 1220 /** 1221 * Calls {@link ItemListener#itemStateChanged} on each ItemListener in 1222 * the button's listener list. 1223 * 1224 * @param e The event signifying that the button's model changed state 1225 */ 1226 protected void fireItemStateChanged(ItemEvent e) 1227 { 1228 e.setSource(this); 1229 ItemListener[] listeners = getItemListeners(); 1230 1231 for (int i = 0; i < listeners.length; i++) 1232 listeners[i].itemStateChanged(e); 1233 } 1234 1235 /** 1236 * Calls {@link ActionListener#actionPerformed} on each {@link 1237 * ActionListener} in the button's listener list. 1238 * 1239 * @param e The event signifying that the button's model was clicked 1240 */ 1241 protected void fireActionPerformed(ActionEvent e) 1242 { 1243 // Dispatch a copy of the given ActionEvent in order to 1244 // set the source and action command correctly. 1245 ActionEvent ae = new ActionEvent( 1246 this, 1247 e.getID(), 1248 getActionCommand(), 1249 e.getWhen(), 1250 e.getModifiers()); 1251 1252 ActionListener[] listeners = getActionListeners(); 1253 1254 for (int i = 0; i < listeners.length; i++) 1255 listeners[i].actionPerformed(ae); 1256 } 1257 1258 /** 1259 * Calls {@link ChangeListener#stateChanged} on each {@link ChangeListener} 1260 * in the button's listener list. 1261 */ 1262 protected void fireStateChanged() 1263 { 1264 ChangeListener[] listeners = getChangeListeners(); 1265 1266 for (int i = 0; i < listeners.length; i++) 1267 listeners[i].stateChanged(changeEvent); 1268 } 1269 1270 /** 1271 * Get the current keyboard mnemonic value. This value corresponds to a 1272 * single key code (one of the {@link java.awt.event.KeyEvent} VK_* 1273 * codes) and is used to activate the button when pressed in conjunction 1274 * with the "mouseless modifier" of the button's look and feel class, and 1275 * when focus is in one of the button's ancestors. 1276 * 1277 * @return The button's current keyboard mnemonic 1278 */ 1279 public int getMnemonic() 1280 { 1281 ButtonModel mod = getModel(); 1282 if (mod != null) 1283 return mod.getMnemonic(); 1284 return -1; 1285 } 1286 1287 /** 1288 * Set the current keyboard mnemonic value. This value corresponds to a 1289 * single key code (one of the {@link java.awt.event.KeyEvent} VK_* 1290 * codes) and is used to activate the button when pressed in conjunction 1291 * with the "mouseless modifier" of the button's look and feel class, and 1292 * when focus is in one of the button's ancestors. 1293 * 1294 * @param mne A new mnemonic to use for the button 1295 */ 1296 public void setMnemonic(char mne) 1297 { 1298 setMnemonic((int) mne); 1299 } 1300 1301 /** 1302 * Set the current keyboard mnemonic value. This value corresponds to a 1303 * single key code (one of the {@link java.awt.event.KeyEvent} VK_* 1304 * codes) and is used to activate the button when pressed in conjunction 1305 * with the "mouseless modifier" of the button's look and feel class, and 1306 * when focus is in one of the button's ancestors. 1307 * 1308 * @param mne A new mnemonic to use for the button 1309 */ 1310 public void setMnemonic(int mne) 1311 { 1312 ButtonModel mod = getModel(); 1313 int old = -1; 1314 if (mod != null) 1315 old = mod.getMnemonic(); 1316 1317 if (old != mne) 1318 { 1319 if (mod != null) 1320 mod.setMnemonic(mne); 1321 1322 if (text != null && !text.equals("")) 1323 { 1324 // Since lower case char = upper case char for 1325 // mnemonic, we will convert both text and mnemonic 1326 // to upper case before checking if mnemonic character occurs 1327 // in the menu item text. 1328 int upperCaseMne = Character.toUpperCase((char) mne); 1329 String upperCaseText = text.toUpperCase(); 1330 setDisplayedMnemonicIndex(upperCaseText.indexOf(upperCaseMne)); 1331 } 1332 1333 firePropertyChange(MNEMONIC_CHANGED_PROPERTY, old, mne); 1334 revalidate(); 1335 repaint(); 1336 } 1337 } 1338 1339 /** 1340 * Sets the button's mnemonic index. The mnemonic index is a hint to the 1341 * look and feel class, suggesting which character in the button's label 1342 * should be underlined when drawing the label. If the mnemonic index is 1343 * -1, no mnemonic will be displayed. 1344 * 1345 * If no mnemonic index is set, the button will choose a mnemonic index 1346 * by default, which will be the first occurrence of the mnemonic 1347 * character in the button's text. 1348 * 1349 * @param index An offset into the "text" property of the button 1350 * @throws IllegalArgumentException If <code>index</code> is not within the 1351 * range of legal offsets for the "text" property of the button. 1352 * @since 1.4 1353 */ 1354 1355 public void setDisplayedMnemonicIndex(int index) 1356 { 1357 if (index < -1 || (text != null && index >= text.length())) 1358 throw new IllegalArgumentException(); 1359 1360 mnemonicIndex = index; 1361 } 1362 1363 /** 1364 * Get the button's mnemonic index, which is an offset into the button's 1365 * "text" property. The character specified by this offset should be 1366 * underlined when the look and feel class draws this button. 1367 * 1368 * @return An index into the button's "text" property 1369 */ 1370 public int getDisplayedMnemonicIndex() 1371 { 1372 return mnemonicIndex; 1373 } 1374 1375 1376 /** 1377 * Set the "rolloverEnabled" property. When rollover is enabled, and the 1378 * look and feel supports it, the button will change its icon to 1379 * rolloverIcon, when the mouse passes over it. 1380 * 1381 * @param r Whether or not to enable rollover icon changes 1382 */ 1383 public void setRolloverEnabled(boolean r) 1384 { 1385 clientRolloverEnabledSet = true; 1386 if (rollOverEnabled != r) 1387 { 1388 rollOverEnabled = r; 1389 firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r); 1390 revalidate(); 1391 repaint(); 1392 } 1393 } 1394 1395 /** 1396 * Returns whether or not rollover icon changes are enabled on the 1397 * button. 1398 * 1399 * @return The state of the "rolloverEnabled" property 1400 */ 1401 public boolean isRolloverEnabled() 1402 { 1403 return rollOverEnabled; 1404 } 1405 1406 /** 1407 * Set the value of the button's "selected" property. Selection is only 1408 * meaningful for toggle-type buttons (check boxes, radio buttons). 1409 * 1410 * @param s New value for the property 1411 */ 1412 public void setSelected(boolean s) 1413 { 1414 ButtonModel mod = getModel(); 1415 if (mod != null) 1416 mod.setSelected(s); 1417 } 1418 1419 /** 1420 * Get the value of the button's "selected" property. Selection is only 1421 * meaningful for toggle-type buttons (check boxes, radio buttons). 1422 * 1423 * @return The value of the property 1424 */ 1425 public boolean isSelected() 1426 { 1427 ButtonModel mod = getModel(); 1428 if (mod != null) 1429 return mod.isSelected(); 1430 return false; 1431 } 1432 1433 /** 1434 * Enables or disables the button. A button will neither be selectable 1435 * nor preform any actions unless it is enabled. 1436 * 1437 * @param b Whether or not to enable the button 1438 */ 1439 public void setEnabled(boolean b) 1440 { 1441 // Do nothing if state does not change. 1442 if (b == isEnabled()) 1443 return; 1444 super.setEnabled(b); 1445 setFocusable(b); 1446 ButtonModel mod = getModel(); 1447 if (mod != null) 1448 mod.setEnabled(b); 1449 } 1450 1451 /** 1452 * Set the horizontal alignment of the button's text and icon. The 1453 * alignment is a numeric constant from {@link SwingConstants}. It must 1454 * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, 1455 * <code>LEADING</code> or <code>TRAILING</code>. The default is 1456 * <code>CENTER</code>. 1457 * 1458 * @return The current horizontal alignment 1459 * 1460 * @see #setHorizontalAlignment(int) 1461 */ 1462 public int getHorizontalAlignment() 1463 { 1464 return horizontalAlignment; 1465 } 1466 1467 /** 1468 * Set the horizontal alignment of the button's text and icon. The 1469 * alignment is a numeric constant from {@link SwingConstants}. It must 1470 * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, 1471 * <code>LEADING</code> or <code>TRAILING</code>. The default is 1472 * <code>CENTER</code>. 1473 * 1474 * @param a The new horizontal alignment 1475 * @throws IllegalArgumentException If alignment is not one of the legal 1476 * constants. 1477 * 1478 * @see #getHorizontalAlignment() 1479 */ 1480 public void setHorizontalAlignment(int a) 1481 { 1482 if (horizontalAlignment == a) 1483 return; 1484 if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING 1485 && a != TRAILING) 1486 throw new IllegalArgumentException("Invalid alignment."); 1487 int old = horizontalAlignment; 1488 horizontalAlignment = a; 1489 firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a); 1490 revalidate(); 1491 repaint(); 1492 } 1493 1494 /** 1495 * Get the horizontal position of the button's text relative to its 1496 * icon. The position is a numeric constant from {@link 1497 * SwingConstants}. It must be one of: <code>RIGHT</code>, 1498 * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or 1499 * <code>TRAILING</code>. The default is <code>TRAILING</code>. 1500 * 1501 * @return The current horizontal text position 1502 */ 1503 public int getHorizontalTextPosition() 1504 { 1505 return horizontalTextPosition; 1506 } 1507 1508 /** 1509 * Set the horizontal position of the button's text relative to its 1510 * icon. The position is a numeric constant from {@link 1511 * SwingConstants}. It must be one of: <code>RIGHT</code>, 1512 * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or 1513 * <code>TRAILING</code>. The default is <code>TRAILING</code>. 1514 * 1515 * @param t The new horizontal text position 1516 * @throws IllegalArgumentException If position is not one of the legal 1517 * constants. 1518 */ 1519 public void setHorizontalTextPosition(int t) 1520 { 1521 if (horizontalTextPosition == t) 1522 return; 1523 if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING 1524 && t != TRAILING) 1525 throw new IllegalArgumentException("Invalid alignment."); 1526 1527 int old = horizontalTextPosition; 1528 horizontalTextPosition = t; 1529 firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t); 1530 revalidate(); 1531 repaint(); 1532 } 1533 1534 /** 1535 * Get the vertical alignment of the button's text and icon. The 1536 * alignment is a numeric constant from {@link SwingConstants}. It must 1537 * be one of: <code>CENTER</code>, <code>TOP</code>, or 1538 * <code>BOTTOM</code>. The default is <code>CENTER</code>. 1539 * 1540 * @return The current vertical alignment 1541 * 1542 * @see #setVerticalAlignment(int) 1543 */ 1544 public int getVerticalAlignment() 1545 { 1546 return verticalAlignment; 1547 } 1548 1549 /** 1550 * Set the vertical alignment of the button's text and icon. The 1551 * alignment is a numeric constant from {@link SwingConstants}. It must 1552 * be one of: <code>CENTER</code>, <code>TOP</code>, or 1553 * <code>BOTTOM</code>. The default is <code>CENTER</code>. 1554 * 1555 * @param a The new vertical alignment 1556 * @throws IllegalArgumentException If alignment is not one of the legal 1557 * constants. 1558 * 1559 * @see #getVerticalAlignment() 1560 */ 1561 public void setVerticalAlignment(int a) 1562 { 1563 if (verticalAlignment == a) 1564 return; 1565 if (a != TOP && a != CENTER && a != BOTTOM) 1566 throw new IllegalArgumentException("Invalid alignment."); 1567 1568 int old = verticalAlignment; 1569 verticalAlignment = a; 1570 firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a); 1571 revalidate(); 1572 repaint(); 1573 } 1574 1575 /** 1576 * Get the vertical position of the button's text relative to its 1577 * icon. The alignment is a numeric constant from {@link 1578 * SwingConstants}. It must be one of: <code>CENTER</code>, 1579 * <code>TOP</code>, or <code>BOTTOM</code>. The default is 1580 * <code>CENTER</code>. 1581 * 1582 * @return The current vertical position 1583 */ 1584 public int getVerticalTextPosition() 1585 { 1586 return verticalTextPosition; 1587 } 1588 1589 /** 1590 * Set the vertical position of the button's text relative to its 1591 * icon. The alignment is a numeric constant from {@link 1592 * SwingConstants}. It must be one of: <code>CENTER</code>, 1593 * <code>TOP</code>, or <code>BOTTOM</code>. The default is 1594 * <code>CENTER</code>. 1595 * 1596 * @param t The new vertical position 1597 * @throws IllegalArgumentException If position is not one of the legal 1598 * constants. 1599 */ 1600 public void setVerticalTextPosition(int t) 1601 { 1602 if (verticalTextPosition == t) 1603 return; 1604 if (t != TOP && t != CENTER && t != BOTTOM) 1605 throw new IllegalArgumentException("Invalid alignment."); 1606 1607 int old = verticalTextPosition; 1608 verticalTextPosition = t; 1609 firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t); 1610 revalidate(); 1611 repaint(); 1612 } 1613 1614 /** 1615 * Set the value of the "borderPainted" property. If set to 1616 * <code>false</code>, the button's look and feel class should not paint 1617 * a border for the button. The default is <code>true</code>. 1618 * 1619 * @return The current value of the property. 1620 */ 1621 public boolean isBorderPainted() 1622 { 1623 return borderPainted; 1624 } 1625 1626 /** 1627 * Set the value of the "borderPainted" property. If set to 1628 * <code>false</code>, the button's look and feel class should not paint 1629 * a border for the button. The default is <code>true</code>. 1630 * 1631 * @param b The new value of the property. 1632 */ 1633 public void setBorderPainted(boolean b) 1634 { 1635 clientBorderPaintedSet = true; 1636 if (borderPainted == b) 1637 return; 1638 boolean old = borderPainted; 1639 borderPainted = b; 1640 firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b); 1641 revalidate(); 1642 repaint(); 1643 } 1644 1645 /** 1646 * Get the value of the "action" property. 1647 * 1648 * @return The current value of the "action" property 1649 */ 1650 public Action getAction() 1651 { 1652 return action; 1653 } 1654 1655 /** 1656 * <p>Set the button's "action" property, subscribing the new action to the 1657 * button, as an ActionListener, if it is not already subscribed. The old 1658 * Action, if it exists, is unsubscribed, and the button is unsubscribed 1659 * from the old Action if it was previously subscribed as a 1660 * PropertyChangeListener.</p> 1661 * 1662 * <p>This method also configures several of the button's properties from 1663 * the Action, by calling {@link #configurePropertiesFromAction}, and 1664 * subscribes the button to the Action as a PropertyChangeListener. 1665 * Subsequent changes to the Action will thus reconfigure the button 1666 * automatically.</p> 1667 * 1668 * @param a The new value of the "action" property 1669 */ 1670 public void setAction(Action a) 1671 { 1672 if (action != null) 1673 { 1674 action.removePropertyChangeListener(actionPropertyChangeListener); 1675 removeActionListener(action); 1676 if (actionPropertyChangeListener != null) 1677 { 1678 action.removePropertyChangeListener(actionPropertyChangeListener); 1679 actionPropertyChangeListener = null; 1680 } 1681 } 1682 1683 Action old = action; 1684 action = a; 1685 configurePropertiesFromAction(action); 1686 if (action != null) 1687 { 1688 actionPropertyChangeListener = createActionPropertyChangeListener(a); 1689 action.addPropertyChangeListener(actionPropertyChangeListener); 1690 addActionListener(action); 1691 } 1692 } 1693 1694 /** 1695 * Return the button's default "icon" property. 1696 * 1697 * @return The current default icon 1698 */ 1699 public Icon getIcon() 1700 { 1701 return default_icon; 1702 } 1703 1704 /** 1705 * Set the button's default "icon" property. This icon is used as a basis 1706 * for the pressed and disabled icons, if none are explicitly set. 1707 * 1708 * @param i The new default icon 1709 */ 1710 public void setIcon(Icon i) 1711 { 1712 if (default_icon == i) 1713 return; 1714 1715 Icon old = default_icon; 1716 default_icon = i; 1717 firePropertyChange(ICON_CHANGED_PROPERTY, old, i); 1718 revalidate(); 1719 repaint(); 1720 } 1721 1722 /** 1723 * Return the button's "text" property. This property is synonymous with 1724 * the "label" property. 1725 * 1726 * @return The current "text" property 1727 */ 1728 public String getText() 1729 { 1730 return text; 1731 } 1732 1733 /** 1734 * Set the button's "label" property. This property is synonymous with the 1735 * "text" property. 1736 * 1737 * @param label The new "label" property 1738 * 1739 * @deprecated use <code>setText(text)</code> 1740 */ 1741 public void setLabel(String label) 1742 { 1743 setText(label); 1744 } 1745 1746 /** 1747 * Return the button's "label" property. This property is synonymous with 1748 * the "text" property. 1749 * 1750 * @return The current "label" property 1751 * 1752 * @deprecated use <code>getText()</code> 1753 */ 1754 public String getLabel() 1755 { 1756 return getText(); 1757 } 1758 1759 /** 1760 * Set the button's "text" property. This property is synonymous with the 1761 * "label" property. 1762 * 1763 * @param t The new "text" property 1764 */ 1765 public void setText(String t) 1766 { 1767 if (text == t) 1768 return; 1769 1770 String old = text; 1771 text = t; 1772 firePropertyChange(TEXT_CHANGED_PROPERTY, old, t); 1773 revalidate(); 1774 repaint(); 1775 } 1776 1777 /** 1778 * Set the value of the {@link #iconTextGap} property. 1779 * 1780 * @param i The new value of the property 1781 * 1782 * @since 1.4 1783 */ 1784 public void setIconTextGap(int i) 1785 { 1786 clientIconTextGapSet = true; 1787 if (iconTextGap == i) 1788 return; 1789 1790 int old = iconTextGap; 1791 iconTextGap = i; 1792 firePropertyChange("iconTextGap", new Integer(old), new Integer(i)); 1793 revalidate(); 1794 repaint(); 1795 } 1796 1797 /** 1798 * Get the value of the {@link #iconTextGap} property. 1799 * 1800 * @return The current value of the property 1801 * 1802 * @since 1.4 1803 */ 1804 public int getIconTextGap() 1805 { 1806 return iconTextGap; 1807 } 1808 1809 /** 1810 * Return the button's "margin" property, which is an {@link Insets} object 1811 * describing the distance between the button's border and its text and 1812 * icon. 1813 * 1814 * @return The current "margin" property 1815 */ 1816 public Insets getMargin() 1817 { 1818 return margin; 1819 } 1820 1821 /** 1822 * Set the button's "margin" property, which is an {@link Insets} object 1823 * describing the distance between the button's border and its text and 1824 * icon. 1825 * 1826 * @param m The new "margin" property 1827 */ 1828 public void setMargin(Insets m) 1829 { 1830 if (margin == m) 1831 return; 1832 1833 Insets old = margin; 1834 margin = m; 1835 firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m); 1836 revalidate(); 1837 repaint(); 1838 } 1839 1840 /** 1841 * Return the button's "pressedIcon" property. The look and feel class 1842 * should paint this icon when the "pressed" property of the button's 1843 * {@link ButtonModel} is <code>true</code>. This property may be 1844 * <code>null</code>, in which case the default icon is used. 1845 * 1846 * @return The current "pressedIcon" property 1847 */ 1848 public Icon getPressedIcon() 1849 { 1850 return pressed_icon; 1851 } 1852 1853 /** 1854 * Set the button's "pressedIcon" property. The look and feel class 1855 * should paint this icon when the "pressed" property of the button's 1856 * {@link ButtonModel} is <code>true</code>. This property may be 1857 * <code>null</code>, in which case the default icon is used. 1858 * 1859 * @param pressedIcon The new "pressedIcon" property 1860 */ 1861 public void setPressedIcon(Icon pressedIcon) 1862 { 1863 if (pressed_icon == pressedIcon) 1864 return; 1865 1866 Icon old = pressed_icon; 1867 pressed_icon = pressedIcon; 1868 firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon); 1869 revalidate(); 1870 repaint(); 1871 } 1872 1873 /** 1874 * Return the button's "disabledIcon" property. The look and feel class 1875 * should paint this icon when the "enabled" property of the button's 1876 * {@link ButtonModel} is <code>false</code>. This property may be 1877 * <code>null</code>, in which case an icon is constructed, based on the 1878 * default icon. 1879 * 1880 * @return The current "disabledIcon" property 1881 */ 1882 public Icon getDisabledIcon() 1883 { 1884 if (disabledIcon == null && default_icon instanceof ImageIcon) 1885 { 1886 Image iconImage = ((ImageIcon) default_icon).getImage(); 1887 Image grayImage = GrayFilter.createDisabledImage(iconImage); 1888 disabledIcon = new ImageIcon(grayImage); 1889 } 1890 1891 return disabledIcon; 1892 } 1893 1894 /** 1895 * Set the button's "disabledIcon" property. The look and feel class should 1896 * paint this icon when the "enabled" property of the button's {@link 1897 * ButtonModel} is <code>false</code>. This property may be 1898 * <code>null</code>, in which case an icon is constructed, based on the 1899 * default icon. 1900 * 1901 * @param d The new "disabledIcon" property 1902 */ 1903 public void setDisabledIcon(Icon d) 1904 { 1905 if (disabledIcon == d) 1906 return; 1907 Icon old = disabledIcon; 1908 disabledIcon = d; 1909 firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d); 1910 revalidate(); 1911 repaint(); 1912 } 1913 1914 /** 1915 * Return the button's "paintFocus" property. This property controls 1916 * whether or not the look and feel class will paint a special indicator 1917 * of focus state for the button. If it is false, the button still paints 1918 * when focused, but no special decoration is painted to indicate the 1919 * presence of focus. 1920 * 1921 * @return The current "paintFocus" property 1922 */ 1923 public boolean isFocusPainted() 1924 { 1925 return focusPainted; 1926 } 1927 1928 /** 1929 * Set the button's "paintFocus" property. This property controls whether 1930 * or not the look and feel class will paint a special indicator of focus 1931 * state for the button. If it is false, the button still paints when 1932 * focused, but no special decoration is painted to indicate the presence 1933 * of focus. 1934 * 1935 * @param p The new "paintFocus" property 1936 */ 1937 public void setFocusPainted(boolean p) 1938 { 1939 if (focusPainted == p) 1940 return; 1941 1942 boolean old = focusPainted; 1943 focusPainted = p; 1944 firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p); 1945 revalidate(); 1946 repaint(); 1947 } 1948 1949 /** 1950 * Verifies that a particular key is one of the valid constants used for 1951 * describing horizontal alignment and positioning. The valid constants 1952 * are the following members of {@link SwingConstants}: 1953 * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, 1954 * <code>LEADING</code> or <code>TRAILING</code>. 1955 * 1956 * @param key The key to check 1957 * @param exception A message to include in an IllegalArgumentException 1958 * 1959 * @return the value of key 1960 * 1961 * @throws IllegalArgumentException If key is not one of the valid constants 1962 * 1963 * @see #setHorizontalTextPosition(int) 1964 * @see #setHorizontalAlignment(int) 1965 */ 1966 protected int checkHorizontalKey(int key, String exception) 1967 { 1968 switch (key) 1969 { 1970 case SwingConstants.RIGHT: 1971 case SwingConstants.LEFT: 1972 case SwingConstants.CENTER: 1973 case SwingConstants.LEADING: 1974 case SwingConstants.TRAILING: 1975 break; 1976 default: 1977 throw new IllegalArgumentException(exception); 1978 } 1979 return key; 1980 } 1981 1982 /** 1983 * Verifies that a particular key is one of the valid constants used for 1984 * describing vertical alignment and positioning. The valid constants are 1985 * the following members of {@link SwingConstants}: <code>TOP</code>, 1986 * <code>BOTTOM</code> or <code>CENTER</code>. 1987 * 1988 * @param key The key to check 1989 * @param exception A message to include in an IllegalArgumentException 1990 * 1991 * @return the value of key 1992 * 1993 * @throws IllegalArgumentException If key is not one of the valid constants 1994 * 1995 * @see #setVerticalTextPosition(int) 1996 * @see #setVerticalAlignment(int) 1997 */ 1998 protected int checkVerticalKey(int key, String exception) 1999 { 2000 switch (key) 2001 { 2002 case SwingConstants.TOP: 2003 case SwingConstants.BOTTOM: 2004 case SwingConstants.CENTER: 2005 break; 2006 default: 2007 throw new IllegalArgumentException(exception); 2008 } 2009 return key; 2010 } 2011 2012 /** 2013 * Configure various properties of the button by reading properties 2014 * of an {@link Action}. The mapping of properties is as follows: 2015 * 2016 * <table> 2017 * 2018 * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr> 2019 * 2020 * <tr><td>NAME </td> <td>text </td></tr> 2021 * <tr><td>SMALL_ICON </td> <td>icon </td></tr> 2022 * <tr><td>SHORT_DESCRIPTION </td> <td>toolTipText </td></tr> 2023 * <tr><td>MNEMONIC_KEY </td> <td>mnemonic </td></tr> 2024 * <tr><td>ACTION_COMMAND_KEY </td> <td>actionCommand </td></tr> 2025 * 2026 * </table> 2027 * 2028 * <p>In addition, this method always sets the button's "enabled" property to 2029 * the value of the Action's "enabled" property.</p> 2030 * 2031 * <p>If the provided Action is <code>null</code>, the text, icon, and 2032 * toolTipText properties of the button are set to <code>null</code>, and 2033 * the "enabled" property is set to <code>true</code>; the mnemonic and 2034 * actionCommand properties are unchanged.</p> 2035 * 2036 * @param a An Action to configure the button from 2037 */ 2038 protected void configurePropertiesFromAction(Action a) 2039 { 2040 if (a == null) 2041 { 2042 setText(null); 2043 setIcon(null); 2044 setEnabled(true); 2045 setToolTipText(null); 2046 } 2047 else 2048 { 2049 setText((String) (a.getValue(Action.NAME))); 2050 setIcon((Icon) (a.getValue(Action.SMALL_ICON))); 2051 setEnabled(a.isEnabled()); 2052 setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION))); 2053 if (a.getValue(Action.MNEMONIC_KEY) != null) 2054 setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue()); 2055 String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY)); 2056 2057 // Set actionCommand to button's text by default if it is not specified 2058 if (actionCommand != null) 2059 setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY))); 2060 else 2061 setActionCommand(getText()); 2062 } 2063 } 2064 2065 /** 2066 * <p>A factory method which should return an {@link ActionListener} that 2067 * propagates events from the button's {@link ButtonModel} to any of the 2068 * button's ActionListeners. By default, this is an inner class which 2069 * calls {@link AbstractButton#fireActionPerformed} with a modified copy 2070 * of the incoming model {@link ActionEvent}.</p> 2071 * 2072 * <p>The button calls this method during construction, stores the 2073 * resulting ActionListener in its <code>actionListener</code> member 2074 * field, and subscribes it to the button's model. If the button's model 2075 * is changed, this listener is unsubscribed from the old model and 2076 * subscribed to the new one.</p> 2077 * 2078 * @return A new ActionListener 2079 */ 2080 protected ActionListener createActionListener() 2081 { 2082 return getEventHandler(); 2083 } 2084 2085 /** 2086 * <p>A factory method which should return a {@link PropertyChangeListener} 2087 * that accepts changes to the specified {@link Action} and reconfigure 2088 * the {@link AbstractButton}, by default using the {@link 2089 * #configurePropertiesFromAction} method.</p> 2090 * 2091 * <p>The button calls this method whenever a new Action is assigned to 2092 * the button's "action" property, via {@link #setAction}, and stores the 2093 * resulting PropertyChangeListener in its 2094 * <code>actionPropertyChangeListener</code> member field. The button 2095 * then subscribes the listener to the button's new action. If the 2096 * button's action is changed subsequently, the listener is unsubscribed 2097 * from the old action and subscribed to the new one.</p> 2098 * 2099 * @param a The Action which will be listened to, and which should be 2100 * the same as the source of any PropertyChangeEvents received by the 2101 * new listener returned from this method. 2102 * 2103 * @return A new PropertyChangeListener 2104 */ 2105 protected PropertyChangeListener createActionPropertyChangeListener(Action a) 2106 { 2107 return new PropertyChangeListener() 2108 { 2109 public void propertyChange(PropertyChangeEvent e) 2110 { 2111 Action act = (Action) (e.getSource()); 2112 if (e.getPropertyName().equals("enabled")) 2113 setEnabled(act.isEnabled()); 2114 else if (e.getPropertyName().equals(Action.NAME)) 2115 setText((String) (act.getValue(Action.NAME))); 2116 else if (e.getPropertyName().equals(Action.SMALL_ICON)) 2117 setIcon((Icon) (act.getValue(Action.SMALL_ICON))); 2118 else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION)) 2119 setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION))); 2120 else if (e.getPropertyName().equals(Action.MNEMONIC_KEY)) 2121 if (act.getValue(Action.MNEMONIC_KEY) != null) 2122 setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY))) 2123 .intValue()); 2124 else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY)) 2125 setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY))); 2126 } 2127 }; 2128 } 2129 2130 /** 2131 * <p>Factory method which creates a {@link ChangeListener}, used to 2132 * subscribe to ChangeEvents from the button's model. Subclasses of 2133 * AbstractButton may wish to override the listener used to subscribe to 2134 * such ChangeEvents. By default, the listener just propagates the 2135 * {@link ChangeEvent} to the button's ChangeListeners, via the {@link 2136 * AbstractButton#fireStateChanged} method.</p> 2137 * 2138 * <p>The button calls this method during construction, stores the 2139 * resulting ChangeListener in its <code>changeListener</code> member 2140 * field, and subscribes it to the button's model. If the button's model 2141 * is changed, this listener is unsubscribed from the old model and 2142 * subscribed to the new one.</p> 2143 * 2144 * @return The new ChangeListener 2145 */ 2146 protected ChangeListener createChangeListener() 2147 { 2148 return getEventHandler(); 2149 } 2150 2151 /** 2152 * <p>Factory method which creates a {@link ItemListener}, used to 2153 * subscribe to ItemEvents from the button's model. Subclasses of 2154 * AbstractButton may wish to override the listener used to subscribe to 2155 * such ItemEvents. By default, the listener just propagates the 2156 * {@link ItemEvent} to the button's ItemListeners, via the {@link 2157 * AbstractButton#fireItemStateChanged} method.</p> 2158 * 2159 * <p>The button calls this method during construction, stores the 2160 * resulting ItemListener in its <code>changeListener</code> member 2161 * field, and subscribes it to the button's model. If the button's model 2162 * is changed, this listener is unsubscribed from the old model and 2163 * subscribed to the new one.</p> 2164 * 2165 * <p>Note that ItemEvents are only generated from the button's model 2166 * when the model's <em>selected</em> property changes. If you want to 2167 * subscribe to other properties of the model, you must subscribe to 2168 * ChangeEvents. 2169 * 2170 * @return The new ItemListener 2171 */ 2172 protected ItemListener createItemListener() 2173 { 2174 return getEventHandler(); 2175 } 2176 2177 /** 2178 * Programmatically perform a "click" on the button: arming, pressing, 2179 * waiting, un-pressing, and disarming the model. 2180 */ 2181 public void doClick() 2182 { 2183 doClick(100); 2184 } 2185 2186 /** 2187 * Programmatically perform a "click" on the button: arming, pressing, 2188 * waiting, un-pressing, and disarming the model. 2189 * 2190 * @param pressTime The number of milliseconds to wait in the pressed state 2191 */ 2192 public void doClick(int pressTime) 2193 { 2194 ButtonModel mod = getModel(); 2195 if (mod != null) 2196 { 2197 mod.setArmed(true); 2198 mod.setPressed(true); 2199 try 2200 { 2201 java.lang.Thread.sleep(pressTime); 2202 } 2203 catch (java.lang.InterruptedException e) 2204 { 2205 // probably harmless 2206 } 2207 mod.setPressed(false); 2208 mod.setArmed(false); 2209 } 2210 } 2211 2212 /** 2213 * Return the button's disabled selected icon. The look and feel class 2214 * should paint this icon when the "enabled" property of the button's model 2215 * is <code>false</code> and its "selected" property is 2216 * <code>true</code>. This icon can be <code>null</code>, in which case 2217 * it is synthesized from the button's selected icon. 2218 * 2219 * @return The current disabled selected icon 2220 */ 2221 public Icon getDisabledSelectedIcon() 2222 { 2223 return disabledSelectedIcon; 2224 } 2225 2226 /** 2227 * Set the button's disabled selected icon. The look and feel class 2228 * should paint this icon when the "enabled" property of the button's model 2229 * is <code>false</code> and its "selected" property is 2230 * <code>true</code>. This icon can be <code>null</code>, in which case 2231 * it is synthesized from the button's selected icon. 2232 * 2233 * @param icon The new disabled selected icon 2234 */ 2235 public void setDisabledSelectedIcon(Icon icon) 2236 { 2237 if (disabledSelectedIcon == icon) 2238 return; 2239 2240 Icon old = disabledSelectedIcon; 2241 disabledSelectedIcon = icon; 2242 firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon); 2243 revalidate(); 2244 repaint(); 2245 } 2246 2247 /** 2248 * Return the button's rollover icon. The look and feel class should 2249 * paint this icon when the "rolloverEnabled" property of the button is 2250 * <code>true</code> and the mouse rolls over the button. 2251 * 2252 * @return The current rollover icon 2253 */ 2254 public Icon getRolloverIcon() 2255 { 2256 return rolloverIcon; 2257 } 2258 2259 /** 2260 * Set the button's rollover icon and sets the <code>rolloverEnabled</code> 2261 * property to <code>true</code>. The look and feel class should 2262 * paint this icon when the "rolloverEnabled" property of the button is 2263 * <code>true</code> and the mouse rolls over the button. 2264 * 2265 * @param r The new rollover icon 2266 */ 2267 public void setRolloverIcon(Icon r) 2268 { 2269 if (rolloverIcon == r) 2270 return; 2271 2272 Icon old = rolloverIcon; 2273 rolloverIcon = r; 2274 firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon); 2275 setRolloverEnabled(true); 2276 revalidate(); 2277 repaint(); 2278 } 2279 2280 /** 2281 * Return the button's rollover selected icon. The look and feel class 2282 * should paint this icon when the "rolloverEnabled" property of the button 2283 * is <code>true</code>, the "selected" property of the button's model is 2284 * <code>true</code>, and the mouse rolls over the button. 2285 * 2286 * @return The current rollover selected icon 2287 */ 2288 public Icon getRolloverSelectedIcon() 2289 { 2290 return rolloverSelectedIcon; 2291 } 2292 2293 /** 2294 * Set the button's rollover selected icon and sets the 2295 * <code>rolloverEnabled</code> property to <code>true</code>. The look and 2296 * feel class should paint this icon when the "rolloverEnabled" property of 2297 * the button is <code>true</code>, the "selected" property of the button's 2298 * model is <code>true</code>, and the mouse rolls over the button. 2299 * 2300 * @param r The new rollover selected icon. 2301 */ 2302 public void setRolloverSelectedIcon(Icon r) 2303 { 2304 if (rolloverSelectedIcon == r) 2305 return; 2306 2307 Icon old = rolloverSelectedIcon; 2308 rolloverSelectedIcon = r; 2309 firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r); 2310 setRolloverEnabled(true); 2311 revalidate(); 2312 repaint(); 2313 } 2314 2315 /** 2316 * Return the button's selected icon. The look and feel class should 2317 * paint this icon when the "selected" property of the button's model is 2318 * <code>true</code>, and either the "rolloverEnabled" property of the 2319 * button is <code>false</code> or the mouse is not currently rolled 2320 * over the button. 2321 * 2322 * @return The current selected icon 2323 */ 2324 public Icon getSelectedIcon() 2325 { 2326 return selectedIcon; 2327 } 2328 2329 /** 2330 * Set the button's selected icon. The look and feel class should 2331 * paint this icon when the "selected" property of the button's model is 2332 * <code>true</code>, and either the "rolloverEnabled" property of the 2333 * button is <code>false</code> or the mouse is not currently rolled 2334 * over the button. 2335 * 2336 * @param s The new selected icon 2337 */ 2338 public void setSelectedIcon(Icon s) 2339 { 2340 if (selectedIcon == s) 2341 return; 2342 2343 Icon old = selectedIcon; 2344 selectedIcon = s; 2345 firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s); 2346 revalidate(); 2347 repaint(); 2348 } 2349 2350 /** 2351 * Returns an single-element array containing the "text" property of the 2352 * button if the "selected" property of the button's model is 2353 * <code>true</code>, otherwise returns <code>null</code>. 2354 * 2355 * @return The button's "selected object" array 2356 */ 2357 public Object[] getSelectedObjects() 2358 { 2359 if (isSelected()) 2360 { 2361 Object[] objs = new Object[1]; 2362 objs[0] = getText(); 2363 return objs; 2364 } 2365 else 2366 { 2367 return null; 2368 } 2369 } 2370 2371 /** 2372 * Called when image data becomes available for one of the button's icons. 2373 * 2374 * @param img The image being updated 2375 * @param infoflags One of the constant codes in {@link ImageObserver} used 2376 * to describe updated portions of an image. 2377 * @param x X coordinate of the region being updated 2378 * @param y Y coordinate of the region being updated 2379 * @param w Width of the region beign updated 2380 * @param h Height of the region being updated 2381 * 2382 * @return <code>true</code> if img is equal to the button's current icon, 2383 * otherwise <code>false</code> 2384 */ 2385 public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, 2386 int h) 2387 { 2388 return current_icon == img; 2389 } 2390 2391 /** 2392 * Returns the value of the button's "contentAreaFilled" property. This 2393 * property indicates whether the area surrounding the text and icon of 2394 * the button should be filled by the look and feel class. If this 2395 * property is <code>false</code>, the look and feel class should leave 2396 * the content area transparent. 2397 * 2398 * @return The current value of the "contentAreaFilled" property 2399 */ 2400 public boolean isContentAreaFilled() 2401 { 2402 return contentAreaFilled; 2403 } 2404 2405 /** 2406 * Sets the value of the button's "contentAreaFilled" property. This 2407 * property indicates whether the area surrounding the text and icon of 2408 * the button should be filled by the look and feel class. If this 2409 * property is <code>false</code>, the look and feel class should leave 2410 * the content area transparent. 2411 * 2412 * @param b The new value of the "contentAreaFilled" property 2413 */ 2414 public void setContentAreaFilled(boolean b) 2415 { 2416 clientContentAreaFilledSet = true; 2417 if (contentAreaFilled == b) 2418 return; 2419 2420 // The JDK sets the opaque property to the value of the contentAreaFilled 2421 // property, so should we do. 2422 setOpaque(b); 2423 boolean old = contentAreaFilled; 2424 contentAreaFilled = b; 2425 firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b); 2426 } 2427 2428 /** 2429 * Paints the button's border, if the button's "borderPainted" property is 2430 * <code>true</code>, by out calling to the button's look and feel class. 2431 * 2432 * @param g The graphics context used to paint the border 2433 */ 2434 protected void paintBorder(Graphics g) 2435 { 2436 if (isBorderPainted()) 2437 super.paintBorder(g); 2438 } 2439 2440 /** 2441 * Returns a string, used only for debugging, which identifies or somehow 2442 * represents this button. The exact value is implementation-defined. 2443 * 2444 * @return A string representation of the button 2445 */ 2446 protected String paramString() 2447 { 2448 StringBuffer sb = new StringBuffer(); 2449 sb.append(super.paramString()); 2450 sb.append(",defaultIcon="); 2451 if (getIcon() != null) 2452 sb.append(getIcon()); 2453 sb.append(",disabledIcon="); 2454 if (getDisabledIcon() != null) 2455 sb.append(getDisabledIcon()); 2456 sb.append(",disabledSelectedIcon="); 2457 if (getDisabledSelectedIcon() != null) 2458 sb.append(getDisabledSelectedIcon()); 2459 sb.append(",margin="); 2460 if (getMargin() != null) 2461 sb.append(getMargin()); 2462 sb.append(",paintBorder=").append(isBorderPainted()); 2463 sb.append(",paintFocus=").append(isFocusPainted()); 2464 sb.append(",pressedIcon="); 2465 if (getPressedIcon() != null) 2466 sb.append(getPressedIcon()); 2467 sb.append(",rolloverEnabled=").append(isRolloverEnabled()); 2468 sb.append(",rolloverIcon="); 2469 if (getRolloverIcon() != null) 2470 sb.append(getRolloverIcon()); 2471 sb.append(",rolloverSelected="); 2472 if (getRolloverSelectedIcon() != null) 2473 sb.append(getRolloverSelectedIcon()); 2474 sb.append(",selectedIcon="); 2475 if (getSelectedIcon() != null) 2476 sb.append(getSelectedIcon()); 2477 sb.append(",text="); 2478 if (getText() != null) 2479 sb.append(getText()); 2480 return sb.toString(); 2481 } 2482 2483 /** 2484 * Set the "UI" property of the button, which is a look and feel class 2485 * responsible for handling the button's input events and painting it. 2486 * 2487 * @param ui The new "UI" property 2488 */ 2489 public void setUI(ButtonUI ui) 2490 { 2491 super.setUI(ui); 2492 } 2493 2494 /** 2495 * Set the "UI" property of the button, which is a look and feel class 2496 * responsible for handling the button's input events and painting it. 2497 * 2498 * @return The current "UI" property 2499 */ 2500 public ButtonUI getUI() 2501 { 2502 return (ButtonUI) ui; 2503 } 2504 2505 /** 2506 * Set the "UI" property to a class constructed, via the {@link 2507 * UIManager}, from the current look and feel. This should be overridden 2508 * for each subclass of AbstractButton, to retrieve a suitable {@link 2509 * ButtonUI} look and feel class. 2510 */ 2511 public void updateUI() 2512 { 2513 // TODO: What to do here? 2514 } 2515 2516 /** 2517 * Returns the current time in milliseconds in which clicks gets coalesced 2518 * into a single <code>ActionEvent</code>. 2519 * 2520 * @return the time in milliseconds 2521 * 2522 * @since 1.4 2523 */ 2524 public long getMultiClickThreshhold() 2525 { 2526 return multiClickThreshhold; 2527 } 2528 2529 /** 2530 * Sets the time in milliseconds in which clicks gets coalesced into a single 2531 * <code>ActionEvent</code>. 2532 * 2533 * @param threshhold the time in milliseconds 2534 * 2535 * @since 1.4 2536 */ 2537 public void setMultiClickThreshhold(long threshhold) 2538 { 2539 if (threshhold < 0) 2540 throw new IllegalArgumentException(); 2541 2542 multiClickThreshhold = threshhold; 2543 } 2544 2545 /** 2546 * Adds the specified component to this AbstractButton. This overrides the 2547 * default in order to install an {@link OverlayLayout} layout manager 2548 * before adding the component. The layout manager is only installed if 2549 * no other layout manager has been installed before. 2550 * 2551 * @param comp the component to be added 2552 * @param constraints constraints for the layout manager 2553 * @param index the index at which the component is added 2554 * 2555 * @since 1.5 2556 */ 2557 protected void addImpl(Component comp, Object constraints, int index) 2558 { 2559 // We use a client property here, so that no extra memory is used in 2560 // the common case with no layout manager. 2561 if (getClientProperty("AbstractButton.customLayoutSet") == null) 2562 setLayout(new OverlayLayout(this)); 2563 super.addImpl(comp, constraints, index); 2564 } 2565 2566 /** 2567 * Sets a layout manager on this AbstractButton. This is overridden in order 2568 * to detect if the application sets a custom layout manager. If no custom 2569 * layout manager is set, {@link #addImpl(Component, Object, int)} installs 2570 * an OverlayLayout before adding a component. 2571 * 2572 * @param layout the layout manager to install 2573 * 2574 * @since 1.5 2575 */ 2576 public void setLayout(LayoutManager layout) 2577 { 2578 // We use a client property here, so that no extra memory is used in 2579 // the common case with no layout manager. 2580 putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE); 2581 super.setLayout(layout); 2582 } 2583 2584 /** 2585 * Helper method for 2586 * {@link LookAndFeel#installProperty(JComponent, String, Object)}. 2587 * 2588 * @param propertyName the name of the property 2589 * @param value the value of the property 2590 * 2591 * @throws IllegalArgumentException if the specified property cannot be set 2592 * by this method 2593 * @throws ClassCastException if the property value does not match the 2594 * property type 2595 * @throws NullPointerException if <code>c</code> or 2596 * <code>propertyValue</code> is <code>null</code> 2597 */ 2598 void setUIProperty(String propertyName, Object value) 2599 { 2600 if (propertyName.equals("borderPainted")) 2601 { 2602 if (! clientBorderPaintedSet) 2603 { 2604 setBorderPainted(((Boolean) value).booleanValue()); 2605 clientBorderPaintedSet = false; 2606 } 2607 } 2608 else if (propertyName.equals("rolloverEnabled")) 2609 { 2610 if (! clientRolloverEnabledSet) 2611 { 2612 setRolloverEnabled(((Boolean) value).booleanValue()); 2613 clientRolloverEnabledSet = false; 2614 } 2615 } 2616 else if (propertyName.equals("iconTextGap")) 2617 { 2618 if (! clientIconTextGapSet) 2619 { 2620 setIconTextGap(((Integer) value).intValue()); 2621 clientIconTextGapSet = false; 2622 } 2623 } 2624 else if (propertyName.equals("contentAreaFilled")) 2625 { 2626 if (! clientContentAreaFilledSet) 2627 { 2628 setContentAreaFilled(((Boolean) value).booleanValue()); 2629 clientContentAreaFilledSet = false; 2630 } 2631 } 2632 else 2633 { 2634 super.setUIProperty(propertyName, value); 2635 } 2636 } 2637 2638 /** 2639 * Returns the combined event handler. The instance is created if 2640 * necessary. 2641 * 2642 * @return the combined event handler 2643 */ 2644 EventHandler getEventHandler() 2645 { 2646 if (eventHandler == null) 2647 eventHandler = new EventHandler(); 2648 return eventHandler; 2649 } 2650 }