Frames | No Frames |
1: /* AbstractButton.java -- Provides basic button functionality. 2: Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.swing; 39: 40: import gnu.java.lang.CPStringBuilder; 41: 42: import java.awt.Component; 43: import java.awt.Graphics; 44: import java.awt.Image; 45: import java.awt.Insets; 46: import java.awt.ItemSelectable; 47: import java.awt.LayoutManager; 48: import java.awt.Point; 49: import java.awt.Rectangle; 50: import java.awt.Shape; 51: import java.awt.event.ActionEvent; 52: import java.awt.event.ActionListener; 53: import java.awt.event.ItemEvent; 54: import java.awt.event.ItemListener; 55: import java.awt.image.ImageObserver; 56: import java.beans.PropertyChangeEvent; 57: import java.beans.PropertyChangeListener; 58: import java.io.Serializable; 59: import java.util.Enumeration; 60: 61: import javax.accessibility.Accessible; 62: import javax.accessibility.AccessibleAction; 63: import javax.accessibility.AccessibleContext; 64: import javax.accessibility.AccessibleIcon; 65: import javax.accessibility.AccessibleRelation; 66: import javax.accessibility.AccessibleRelationSet; 67: import javax.accessibility.AccessibleState; 68: import javax.accessibility.AccessibleStateSet; 69: import javax.accessibility.AccessibleText; 70: import javax.accessibility.AccessibleValue; 71: import javax.swing.event.ChangeEvent; 72: import javax.swing.event.ChangeListener; 73: import javax.swing.plaf.ButtonUI; 74: import javax.swing.plaf.basic.BasicHTML; 75: import javax.swing.text.AttributeSet; 76: import javax.swing.text.BadLocationException; 77: import javax.swing.text.Document; 78: import javax.swing.text.Element; 79: import javax.swing.text.Position; 80: import javax.swing.text.StyledDocument; 81: import javax.swing.text.View; 82: 83: 84: /** 85: * Provides an abstract implementation of common button behaviour, 86: * data model and look & feel. 87: * 88: * <p>This class is supposed to serve as a base class for 89: * several kinds of buttons with similar but non-identical semantics: 90: * toggle buttons (radio buttons and checkboxes), simple push buttons, 91: * menu items, etc.</p> 92: * 93: * <p>Buttons have many properties, some of which are stored in this class 94: * while others are delegated to the button's model. The following properties 95: * are available:</p> 96: * 97: * <table> 98: * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr> 99: * 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: 165: public 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: }