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