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