1. /*
  2. * @(#)AbstractButton.java 1.102 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.awt.image.*;
  11. import java.io.Serializable;
  12. import javax.swing.event.*;
  13. import javax.swing.border.*;
  14. import javax.swing.plaf.*;
  15. import javax.accessibility.*;
  16. /**
  17. * Defines the common behaviors for the JButton, JToggleButton, JCheckbox,
  18. * and the JRadioButton classes.
  19. * <p>
  20. * <strong>Warning:</strong>
  21. * Serialized objects of this class will not be compatible with
  22. * future Swing releases. The current serialization support is appropriate
  23. * for short term storage or RMI between applications running the same
  24. * version of Swing. A future release of Swing will provide support for
  25. * long term persistence.
  26. *
  27. * @version 1.102 11/29/01
  28. * @author Jeff Dinkins
  29. */
  30. public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants {
  31. // *********************************
  32. // ******* Button properties *******
  33. // *********************************
  34. /** Identifies a change in the button model. */
  35. public static final String MODEL_CHANGED_PROPERTY = "model";
  36. /** Identifies a change in the button's text. */
  37. public static final String TEXT_CHANGED_PROPERTY = "text";
  38. /** Identifies a change to the button's mnemonic. */
  39. public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
  40. // Text positioning and alignment
  41. /** Identifies a change in the button's margins. */
  42. public static final String MARGIN_CHANGED_PROPERTY = "margin";
  43. /** Identifies a change in the button's vertical alignment. */
  44. public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY = "verticalAlignment";
  45. /** Identifies a change in the button's horizontal alignment. */
  46. public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY = "horizontalAlignment";
  47. /** Identifies a change in the button's vertical text position. */
  48. public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY = "verticalTextPosition";
  49. /** Identifies a change in the button's horizontal text position. */
  50. public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY = "horizontalTextPosition";
  51. // Paint options
  52. /** Identifies a change to having the border drawn, or having it not drawn. */
  53. public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
  54. /** Identifies a change to having the border highlighted when focused, or not. */
  55. public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
  56. /** Identifies a change in the button's */
  57. public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY = "rolloverEnabled";
  58. /** Identifies a change from rollover enabled to disabled or back to enabled. */
  59. public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY = "contentAreaFilled";
  60. // Icons
  61. /** Identifies a change to the icon that represents the button. */
  62. public static final String ICON_CHANGED_PROPERTY = "icon";
  63. /** Identifies a change to the icon used when the button has been pressed. */
  64. public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
  65. /** Identifies a change to the icon used when the button has been selected. */
  66. public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
  67. /** Identifies a change to the icon used when the cursor is over the button. */
  68. public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
  69. /** Identifies a change to the icon used when the cursror is over the button and it has been selected. */
  70. public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY = "rolloverSelectedIcon";
  71. /** Identifies a change to the icon used when the button has been disabled. */
  72. public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
  73. /** Identifies a change to the icon used when the button has been disabled and selected. */
  74. public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY = "disabledSelectedIcon";
  75. /** The data model that determines the button's state. */
  76. protected ButtonModel model = null;
  77. private String text = ""; // for BeanBox
  78. private Insets margin = null;
  79. private Insets defaultMargin = null;
  80. // Button icons
  81. // PENDING(jeff) - hold icons in an array
  82. private Icon defaultIcon = null;
  83. private Icon pressedIcon = null;
  84. private Icon disabledIcon = null;
  85. private Icon selectedIcon = null;
  86. private Icon disabledSelectedIcon = null;
  87. private Icon rolloverIcon = null;
  88. private Icon rolloverSelectedIcon = null;
  89. // Display properties
  90. private boolean paintBorder = true;
  91. private boolean paintFocus = true;
  92. private boolean rolloverEnabled = false;
  93. private boolean contentAreaFilled = true;
  94. // Icon/Label Alignment
  95. private int verticalAlignment = CENTER;
  96. private int horizontalAlignment = CENTER;
  97. private int verticalTextPosition = CENTER;
  98. private int horizontalTextPosition = TRAILING;
  99. /**
  100. * The button's model listeners.
  101. */
  102. protected ChangeListener changeListener = null;
  103. protected ActionListener actionListener = null;
  104. protected ItemListener itemListener = null;
  105. /**
  106. * Only one ChangeEvent is needed per button instance since the
  107. * event's only state is the source property. The source of events
  108. * generated is always "this".
  109. */
  110. protected transient ChangeEvent changeEvent;
  111. /**
  112. * Returns the button's text.
  113. * @see #setText
  114. */
  115. public String getText() {
  116. return text;
  117. }
  118. /**
  119. * Sets the button's text.
  120. * @param t the string used to set the text
  121. * @see #getText
  122. * @beaninfo
  123. * bound: true
  124. * preferred: true
  125. * attribute: visualUpdate true
  126. * description: The button's text.
  127. */
  128. public void setText(String text) {
  129. String oldValue = this.text;
  130. this.text = text;
  131. firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);
  132. if (accessibleContext != null) {
  133. accessibleContext.firePropertyChange(
  134. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  135. oldValue, text);
  136. }
  137. if (text == null || oldValue == null || !text.equals(oldValue)) {
  138. revalidate();
  139. repaint();
  140. }
  141. }
  142. /**
  143. * Returns the state of the button. True if the
  144. * toggle button is selected, false if it's not.
  145. */
  146. public boolean isSelected() {
  147. return model.isSelected();
  148. }
  149. /**
  150. * Sets the state of the button. Note that this method does not
  151. * trigger an actionEvent. Call doClick() to perform a programatic
  152. * action change.
  153. */
  154. public void setSelected(boolean b) {
  155. boolean oldValue = isSelected();
  156. if (accessibleContext != null && oldValue != b) {
  157. if (b) {
  158. accessibleContext.firePropertyChange(
  159. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  160. null, AccessibleState.SELECTED);
  161. } else {
  162. accessibleContext.firePropertyChange(
  163. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  164. AccessibleState.SELECTED, null);
  165. }
  166. }
  167. model.setSelected(b);
  168. }
  169. /**
  170. * Programatically perform a "click". This does the same
  171. * thing as if the user had pressed and released the button.
  172. */
  173. public void doClick() {
  174. doClick(68);
  175. }
  176. /**
  177. * Programatically perform a "click". This does the same
  178. * thing as if the user had pressed and released the button.
  179. * The button stays visually "pressed" for pressTime milliseconds.
  180. */
  181. public void doClick(int pressTime) {
  182. Dimension size = getSize();
  183. model.setArmed(true);
  184. model.setPressed(true);
  185. paintImmediately(new Rectangle(0,0, size.width, size.height));
  186. try {
  187. Thread.currentThread().sleep(pressTime);
  188. } catch(InterruptedException ie) {
  189. }
  190. model.setPressed(false);
  191. model.setArmed(false);
  192. }
  193. /**
  194. * Sets space for margin between the button's border and
  195. * the label. Setting to null will cause the button to
  196. * use the default margin. The button's default Border
  197. * object will use this value to create the proper margin.
  198. * However, if a non-default border is set on the button,
  199. * it is that Border object's responsibility to create the
  200. * appropriate margin space (else this property will
  201. * effectively be ignored).
  202. *
  203. * @param m the space between the border and the label
  204. *
  205. * @beaninfo
  206. * bound: true
  207. * attribute: visualUpdate true
  208. * description: The space between the button's border and the label.
  209. */
  210. public void setMargin(Insets m) {
  211. // Cache the old margin if it comes from the UI
  212. if(m instanceof UIResource) {
  213. defaultMargin = m;
  214. } else if(margin instanceof UIResource) {
  215. defaultMargin = margin;
  216. }
  217. // If the client passes in a null insets, restore the margin
  218. // from the UI if possible
  219. if(m == null && defaultMargin != null) {
  220. m = defaultMargin;
  221. }
  222. Insets old = margin;
  223. margin = m;
  224. firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
  225. if (old == null || !m.equals(old)) {
  226. revalidate();
  227. repaint();
  228. }
  229. }
  230. /**
  231. * Returns the margin between the button's border and
  232. * the label.
  233. * @see #setMargin
  234. */
  235. public Insets getMargin() {
  236. return margin;
  237. }
  238. /**
  239. * Returns the default icon.
  240. * @see #setIcon
  241. */
  242. public Icon getIcon() {
  243. return defaultIcon;
  244. }
  245. /**
  246. * Sets the button's default icon. This icon is
  247. * also used as the "pressed" and "disabled" icon if
  248. * there is no explicitly set pressed icon.
  249. * @param g the icon used as the default image
  250. * @see #getIcon
  251. * @see #setPressedIcon
  252. * @beaninfo
  253. * bound: true
  254. * attribute: visualUpdate true
  255. * description: The button's default icon
  256. */
  257. public void setIcon(Icon defaultIcon) {
  258. Icon oldValue = this.defaultIcon;
  259. this.defaultIcon = defaultIcon;
  260. firePropertyChange(ICON_CHANGED_PROPERTY, oldValue, defaultIcon);
  261. if (accessibleContext != null) {
  262. accessibleContext.firePropertyChange(
  263. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  264. oldValue, defaultIcon);
  265. }
  266. if (defaultIcon != oldValue) {
  267. if (defaultIcon == null || oldValue == null ||
  268. defaultIcon.getIconWidth() != oldValue.getIconWidth() ||
  269. defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
  270. revalidate();
  271. }
  272. repaint();
  273. }
  274. }
  275. /**
  276. * Returns the pressed icon for the button.
  277. * @see #setPressedIcon
  278. */
  279. public Icon getPressedIcon() {
  280. return pressedIcon;
  281. }
  282. /**
  283. * Sets the pressed icon for the button.
  284. * @param g the icon used as the "pressed" image
  285. * @see #getPressedIcon
  286. * @beaninfo
  287. * bound: true
  288. * attribute: visualUpdate true
  289. * description: The pressed icon for the button.
  290. */
  291. public void setPressedIcon(Icon pressedIcon) {
  292. Icon oldValue = this.pressedIcon;
  293. this.pressedIcon = pressedIcon;
  294. firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, oldValue, pressedIcon);
  295. if (accessibleContext != null) {
  296. accessibleContext.firePropertyChange(
  297. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  298. oldValue, defaultIcon);
  299. }
  300. if (pressedIcon != oldValue) {
  301. if (getModel().isPressed()) {
  302. repaint();
  303. }
  304. }
  305. }
  306. /**
  307. * Returns the selected icon for the button.
  308. * @see #setSelectedIcon
  309. */
  310. public Icon getSelectedIcon() {
  311. return selectedIcon;
  312. }
  313. /**
  314. * Sets the selected icon for the button.
  315. * @param g the icon used as the "selected" image
  316. * @see #getSelectedIcon
  317. * @beaninfo
  318. * bound: true
  319. * attribute: visualUpdate true
  320. * description: The selected icon for the button.
  321. */
  322. public void setSelectedIcon(Icon selectedIcon) {
  323. Icon oldValue = this.selectedIcon;
  324. this.selectedIcon = selectedIcon;
  325. firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, oldValue, selectedIcon);
  326. if (accessibleContext != null) {
  327. accessibleContext.firePropertyChange(
  328. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  329. oldValue, selectedIcon);
  330. }
  331. if (selectedIcon != oldValue) {
  332. if (isSelected()) {
  333. repaint();
  334. }
  335. }
  336. }
  337. /**
  338. * Returns the rollover icon for the button.
  339. * @see #setRolloverIcon
  340. */
  341. public Icon getRolloverIcon() {
  342. return rolloverIcon;
  343. }
  344. /**
  345. * Sets the rollover icon for the button.
  346. * @param g the icon used as the "rollover" image
  347. * @see #getRolloverIcon
  348. * @beaninfo
  349. * bound: true
  350. * attribute: visualUpdate true
  351. * description: The rollover icon for the button.
  352. */
  353. public void setRolloverIcon(Icon rolloverIcon) {
  354. Icon oldValue = this.rolloverIcon;
  355. this.rolloverIcon = rolloverIcon;
  356. firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, oldValue, rolloverIcon);
  357. if (accessibleContext != null) {
  358. accessibleContext.firePropertyChange(
  359. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  360. oldValue, rolloverIcon);
  361. }
  362. setRolloverEnabled(true);
  363. if (rolloverIcon != oldValue) {
  364. // No way to determine whether we are currently in
  365. // a rollover state, so repaint regardless
  366. repaint();
  367. }
  368. }
  369. /**
  370. * Returns the rollover seletion icon for the button.
  371. * @see #setRolloverSelectedIcon
  372. */
  373. public Icon getRolloverSelectedIcon() {
  374. return rolloverSelectedIcon;
  375. }
  376. /**
  377. * Sets the rollover selected icon for the button.
  378. * @param g the icon used as the "selected rollover" image
  379. * @see #getRolloverSelectedIcon
  380. * @beaninfo
  381. * bound: true
  382. * attribute: visualUpdate true
  383. * description: The rollover selected icon for the button.
  384. */
  385. public void setRolloverSelectedIcon(Icon rolloverSelectedIcon) {
  386. Icon oldValue = this.rolloverSelectedIcon;
  387. this.rolloverSelectedIcon = rolloverSelectedIcon;
  388. firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, oldValue, rolloverSelectedIcon);
  389. if (accessibleContext != null) {
  390. accessibleContext.firePropertyChange(
  391. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  392. oldValue, rolloverSelectedIcon);
  393. }
  394. if (rolloverSelectedIcon != oldValue) {
  395. // No way to determine whether we are currently in
  396. // a rollover state, so repaint regardless
  397. if (isSelected()) {
  398. repaint();
  399. }
  400. }
  401. }
  402. /**
  403. * Returns the icon used by the button when it's disabled.
  404. * If not no disabled icon has been set, the button constructs
  405. * one from the default icon.
  406. * PENDING(jeff): the disabled icon really should be created
  407. * (if necesary) by the L&F.
  408. * @see #getPressedIcon
  409. * @see #setDisabledIcon
  410. */
  411. public Icon getDisabledIcon() {
  412. if(disabledIcon == null) {
  413. if(defaultIcon != null
  414. && defaultIcon instanceof ImageIcon) {
  415. disabledIcon = new ImageIcon(
  416. GrayFilter.createDisabledImage(
  417. ((ImageIcon)defaultIcon).getImage()));
  418. }
  419. }
  420. return disabledIcon;
  421. }
  422. /**
  423. * Sets the disabled icon for the button.
  424. * @param g the icon used as the disabled image
  425. * @see #getDisabledIcon
  426. * @beaninfo
  427. * bound: true
  428. * attribute: visualUpdate true
  429. * description: The disabled icon for the button.
  430. */
  431. public void setDisabledIcon(Icon disabledIcon) {
  432. Icon oldValue = this.disabledIcon;
  433. this.disabledIcon = disabledIcon;
  434. firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldValue, disabledIcon);
  435. if (accessibleContext != null) {
  436. accessibleContext.firePropertyChange(
  437. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  438. oldValue, disabledIcon);
  439. }
  440. if (disabledIcon != oldValue) {
  441. if (!isEnabled()) {
  442. repaint();
  443. }
  444. }
  445. }
  446. /**
  447. * Returns the icon used by the button when it's disabled and selected.
  448. * If not no disabled selection icon has been set, the button constructs
  449. * one from the selection icon.
  450. * PENDING(jeff): the disabled selection icon really should be created
  451. * (if necesary) by the L&F.
  452. * @see #getPressedIcon
  453. * @see #setDisabledIcon
  454. */
  455. public Icon getDisabledSelectedIcon() {
  456. if(disabledSelectedIcon == null) {
  457. if(selectedIcon != null && selectedIcon instanceof ImageIcon) {
  458. disabledSelectedIcon = new ImageIcon(
  459. GrayFilter.createDisabledImage(((ImageIcon)selectedIcon).getImage()));
  460. } else {
  461. return disabledIcon;
  462. }
  463. }
  464. return disabledSelectedIcon;
  465. }
  466. /**
  467. * Sets the disabled selection icon for the button.
  468. * @param g the icon used as the disabled selection image
  469. * @see #getDisabledSelectedIcon
  470. */
  471. public void setDisabledSelectedIcon(Icon disabledSelectedIcon) {
  472. Icon oldValue = this.disabledSelectedIcon;
  473. this.disabledSelectedIcon = disabledSelectedIcon;
  474. firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, oldValue, disabledSelectedIcon);
  475. if (accessibleContext != null) {
  476. accessibleContext.firePropertyChange(
  477. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  478. oldValue, disabledSelectedIcon);
  479. }
  480. if (disabledSelectedIcon != oldValue) {
  481. if (disabledSelectedIcon == null || oldValue == null ||
  482. disabledSelectedIcon.getIconWidth() != oldValue.getIconWidth() ||
  483. disabledSelectedIcon.getIconHeight() != oldValue.getIconHeight()) {
  484. revalidate();
  485. }
  486. if (!isEnabled() && isSelected()) {
  487. repaint();
  488. }
  489. }
  490. }
  491. /**
  492. * Returns the vertical alignment of the text and icon.
  493. * Valid keys: CENTER (the default), TOP, BOTTOM
  494. */
  495. public int getVerticalAlignment() {
  496. return verticalAlignment;
  497. }
  498. /**
  499. * Sets the vertical alignment of the icon and text.
  500. * Valid keys: CENTER (the default), TOP, BOTTOM
  501. * @beaninfo
  502. * bound: true
  503. * enum: TOP SwingConstants.TOP
  504. * CENTER SwingConstants.CENTER
  505. * BOTTOM SwingConstants.BOTTOM
  506. * attribute: visualUpdate true
  507. * description: The vertical alignment of the icon and text.
  508. */
  509. public void setVerticalAlignment(int alignment) {
  510. if (alignment == verticalAlignment) return;
  511. int oldValue = verticalAlignment;
  512. verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
  513. firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, oldValue, verticalAlignment); repaint();
  514. }
  515. /**
  516. * Returns the horizontal alignment of the icon and text.
  517. * Valid keys: CENTER (the default), LEFT, RIGHT
  518. */
  519. public int getHorizontalAlignment() {
  520. return horizontalAlignment;
  521. }
  522. /**
  523. * Sets the horizontal alignment of the icon and text.
  524. * Valid keys: CENTER (the default), LEFT, RIGHT, LEADING or TRAILING
  525. * @beaninfo
  526. * bound: true
  527. * enum: LEFT SwingConstants.LEFT
  528. * CENTER SwingConstants.CENTER
  529. * RIGHT SwingConstants.RIGHT
  530. * LEADING SwingConstants.LEADING
  531. * TRAILING SwingConstants.TRAILING
  532. * attribute: visualUpdate true
  533. * description: The horizontal alignment of the icon and text.
  534. */
  535. public void setHorizontalAlignment(int alignment) {
  536. if (alignment == horizontalAlignment) return;
  537. int oldValue = horizontalAlignment;
  538. horizontalAlignment = checkHorizontalKey(alignment,
  539. "horizontalAlignment");
  540. firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY,
  541. oldValue, horizontalAlignment);
  542. repaint();
  543. }
  544. /**
  545. * Returns the vertical position of the text relative to the icon
  546. * Valid keys: CENTER (the default), TOP, BOTTOM
  547. */
  548. public int getVerticalTextPosition() {
  549. return verticalTextPosition;
  550. }
  551. /**
  552. * Sets the vertical position of the text relative to the icon.
  553. * Valid keys: CENTER (the default), TOP, BOTTOM
  554. * @beaninfo
  555. * bound: true
  556. * enum: TOP SwingConstants.TOP
  557. * CENTER SwingConstants.CENTER
  558. * BOTTOM SwingConstants.BOTTOM
  559. * attribute: visualUpdate true
  560. * description: The vertical position of the text relative to the icon.
  561. */
  562. public void setVerticalTextPosition(int textPosition) {
  563. if (textPosition == verticalTextPosition) return;
  564. int oldValue = verticalTextPosition;
  565. verticalTextPosition = checkVerticalKey(textPosition, "verticalTextPosition");
  566. firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, oldValue, verticalTextPosition);
  567. repaint();
  568. }
  569. /**
  570. * Sets the horizontal position of the text relative to the icon.
  571. * Valid keys: RIGHT (the default), LEFT, CENTER
  572. */
  573. public int getHorizontalTextPosition() {
  574. return horizontalTextPosition;
  575. }
  576. /**
  577. * Sets the horizontal position of the text relative to the icon.
  578. * Valid keys: RIGHT (the default), LEFT, CENTER, LEADING, TRAILING
  579. * @exception IllegalArgumentException
  580. * @beaninfo
  581. * bound: true
  582. * enum: LEFT SwingConstants.LEFT
  583. * CENTER SwingConstants.CENTER
  584. * RIGHT SwingConstants.RIGHT
  585. * LEADING SwingConstants.LEADING
  586. * TRAILING SwingConstants.TRAILING
  587. * attribute: visualUpdate true
  588. * description: The horizontal position of the text relative to the icon.
  589. */
  590. public void setHorizontalTextPosition(int textPosition) {
  591. if (textPosition == horizontalTextPosition) return;
  592. int oldValue = horizontalTextPosition;
  593. horizontalTextPosition = checkHorizontalKey(textPosition,
  594. "horizontalTextPosition");
  595. firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY,
  596. oldValue,
  597. horizontalTextPosition);
  598. repaint();
  599. }
  600. /**
  601. * Verify that key is a legal value for the horizontalAlignment properties.
  602. *
  603. * @param key the property value to check
  604. * @param exception the IllegalArgumentException detail message
  605. * @exception IllegalArgumentException if key isn't LEFT, CENTER, RIGHT,
  606. * LEADING or TRAILING.
  607. * @see #setHorizontalTextPosition
  608. * @see #setHorizontalAlignment
  609. */
  610. protected int checkHorizontalKey(int key, String exception) {
  611. if ((key == LEFT) ||
  612. (key == CENTER) ||
  613. (key == RIGHT) ||
  614. (key == LEADING) ||
  615. (key == TRAILING)) {
  616. return key;
  617. } else {
  618. throw new IllegalArgumentException(exception);
  619. }
  620. }
  621. /**
  622. * Ensures that the key is a valid. Throws an IllegalArgument exception
  623. * exception otherwise.
  624. */
  625. protected int checkVerticalKey(int key, String exception) {
  626. if ((key == TOP) || (key == CENTER) || (key == BOTTOM)) {
  627. return key;
  628. } else {
  629. throw new IllegalArgumentException(exception);
  630. }
  631. }
  632. /**
  633. * Sets the action command for this button.
  634. */
  635. public void setActionCommand(String actionCommand) {
  636. getModel().setActionCommand(actionCommand);
  637. }
  638. /**
  639. * Returns the action command for this button.
  640. */
  641. public String getActionCommand() {
  642. String ac = getModel().getActionCommand();
  643. if(ac == null) {
  644. ac = getText();
  645. }
  646. return ac;
  647. }
  648. /**
  649. * Returns whether the border should be painted.
  650. * @see #setBorderPainted
  651. */
  652. public boolean isBorderPainted() {
  653. return paintBorder;
  654. }
  655. /**
  656. * Sets whether the border should be painted.
  657. * @param b if true and border property is not null, the border is painted.
  658. * @see #isBorderPainted
  659. * @beaninfo
  660. * bound: true
  661. * attribute: visualUpdate true
  662. * description: Whether the border should be painted.
  663. */
  664. public void setBorderPainted(boolean b) {
  665. boolean oldValue = paintBorder;
  666. paintBorder = b;
  667. firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, oldValue, paintBorder);
  668. if (b != oldValue) {
  669. revalidate();
  670. repaint();
  671. }
  672. }
  673. /**
  674. * Paint the button's border if BorderPainted property is true.
  675. *
  676. * @see #paint
  677. * @see #setBorder
  678. */
  679. protected void paintBorder(Graphics g) {
  680. if (isBorderPainted()) {
  681. super.paintBorder(g);
  682. }
  683. }
  684. /**
  685. * Returns whether focus should be painted.
  686. * @see #setFocusPainted
  687. */
  688. public boolean isFocusPainted() {
  689. return paintFocus;
  690. }
  691. /**
  692. * Sets whether focus should be painted.
  693. * @param b if true, the focus state is painted.
  694. * @see #isFocusPainted
  695. * @beaninfo
  696. * bound: true
  697. * attribute: visualUpdate true
  698. * description: Whether focus should be painted
  699. */
  700. public void setFocusPainted(boolean b) {
  701. boolean oldValue = paintFocus;
  702. paintFocus = b;
  703. firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, oldValue, paintFocus);
  704. if (b != oldValue && hasFocus()) {
  705. revalidate();
  706. repaint();
  707. }
  708. }
  709. /**
  710. * Checks whether the "content area" of the button should be filled.
  711. * @see #setFocusPainted
  712. */
  713. public boolean isContentAreaFilled() {
  714. return contentAreaFilled;
  715. }
  716. /**
  717. * Sets whether the button should paint the content area
  718. * or leave it transparent. If you wish to have a transparent
  719. * button, for example and icon only button, then you should set
  720. * this to false. Do not call setOpaque(false). Whether the button
  721. * follows the RepaintManager's concept of opacity is L&F depandant.
  722. *
  723. * This function may cause the component's opaque property to change.
  724. *
  725. * The exact behavior of calling this function varies on a
  726. * component-by-component and L&F-by-L&F basis.
  727. *
  728. * @param b if true, rollover effects should be painted.
  729. * @see #isContentAreaFilled
  730. * @see #setOpaque
  731. * @beaninfo
  732. * bound: true
  733. * attribute: visualUpdate true
  734. * description: Whether the button should paint the content area
  735. * or leave it transparent.
  736. */
  737. public void setContentAreaFilled(boolean b) {
  738. boolean oldValue = contentAreaFilled;
  739. contentAreaFilled = b;
  740. firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, oldValue, contentAreaFilled);
  741. if (b != oldValue) {
  742. repaint();
  743. }
  744. }
  745. /**
  746. * Checks whether rollover effects are enabled.
  747. * @see #setFocusPainted
  748. */
  749. public boolean isRolloverEnabled() {
  750. return rolloverEnabled;
  751. }
  752. /**
  753. * Sets whether rollover effects should be enabled.
  754. * @param b if true, rollover effects should be painted.
  755. * @see #isRolloverEnabled
  756. * @beaninfo
  757. * bound: true
  758. * attribute: visualUpdate true
  759. * description: Whether rollover effects should be enabled.
  760. */
  761. public void setRolloverEnabled(boolean b) {
  762. boolean oldValue = rolloverEnabled;
  763. rolloverEnabled = b;
  764. firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, oldValue, rolloverEnabled);
  765. if (b != oldValue) {
  766. repaint();
  767. }
  768. }
  769. /**
  770. * Get the keyboard mnemonic from the the current model
  771. */
  772. public int getMnemonic() {
  773. return model.getMnemonic();
  774. }
  775. /**
  776. * Set the keyboard mnemonic on the current model.
  777. *
  778. * @param mnemonic the key code which represents the mnemonic
  779. * @beaninfo
  780. * bound: true
  781. * attribute: visualUpdate true
  782. * description: the keyboard character mnemonic
  783. */
  784. public void setMnemonic(int mnemonic) {
  785. int oldValue = getMnemonic();
  786. model.setMnemonic(mnemonic);
  787. firePropertyChange(MNEMONIC_CHANGED_PROPERTY, oldValue, mnemonic);
  788. if (mnemonic != oldValue) {
  789. revalidate();
  790. repaint();
  791. }
  792. }
  793. /**
  794. * Specifies the mnemonic value.
  795. *
  796. * @param mnemonic a char specifying the mnemonic value
  797. */
  798. public void setMnemonic(char mnemonic) {
  799. int vk = (int) mnemonic;
  800. if(vk >= 'a' && vk <='z')
  801. vk -= ('a' - 'A');
  802. setMnemonic(vk);
  803. }
  804. /**
  805. * Get the model that this button represents.
  806. * @see #setModel
  807. */
  808. public ButtonModel getModel() {
  809. return model;
  810. }
  811. /**
  812. * Set the model that this button represents.
  813. * @param m the Model
  814. * @see #getModel
  815. * @beaninfo
  816. * bound: true
  817. * description: Model that the Button uses.
  818. */
  819. public void setModel(ButtonModel newModel) {
  820. ButtonModel oldModel = getModel();
  821. if (oldModel != null) {
  822. oldModel.removeChangeListener(changeListener);
  823. oldModel.removeActionListener(actionListener);
  824. changeListener = null;
  825. actionListener = null;
  826. }
  827. model = newModel;
  828. if (newModel != null) {
  829. changeListener = createChangeListener();
  830. actionListener = createActionListener();
  831. itemListener = createItemListener();
  832. newModel.addChangeListener(changeListener);
  833. newModel.addActionListener(actionListener);
  834. newModel.addItemListener(itemListener);
  835. }
  836. firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
  837. if (newModel != oldModel) {
  838. revalidate();
  839. repaint();
  840. }
  841. }
  842. /**
  843. * Returns the button's current UI.
  844. * @see #setUI
  845. */
  846. public ButtonUI getUI() {
  847. return (ButtonUI) ui;
  848. }
  849. /**
  850. * Sets the button's UI.
  851. * @param ui the new ButtonUI
  852. * @see #getUI
  853. */
  854. public void setUI(ButtonUI ui) {
  855. super.setUI(ui);
  856. }
  857. /**
  858. * Gets a new UI object from the default UIFactory. Subtypes of
  859. * AbstractButton should override this to update the UI. For
  860. * example, JButton might do the following:
  861. * setUI((ButtonUI)UIManager.getUI(
  862. * "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI", this));
  863. */
  864. public void updateUI() {
  865. }
  866. /**
  867. * Adds a ChangeListener to the button.
  868. */
  869. public void addChangeListener(ChangeListener l) {
  870. listenerList.add(ChangeListener.class, l);
  871. }
  872. /**
  873. * Removes a ChangeListener from the button.
  874. */
  875. public void removeChangeListener(ChangeListener l) {
  876. listenerList.remove(ChangeListener.class, l);
  877. }
  878. /*
  879. * Notify all listeners that have registered interest for
  880. * notification on this event type. The event instance
  881. * is lazily created using the parameters passed into
  882. * the fire method.
  883. * @see EventListenerList
  884. */
  885. protected void fireStateChanged() {
  886. // Guaranteed to return a non-null array
  887. Object[] listeners = listenerList.getListenerList();
  888. // Process the listeners last to first, notifying
  889. // those that are interested in this event
  890. for (int i = listeners.length-2; i>=0; i-=2) {
  891. if (listeners[i]==ChangeListener.class) {
  892. // Lazily create the event:
  893. if (changeEvent == null)
  894. changeEvent = new ChangeEvent(this);
  895. ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  896. }
  897. }
  898. }
  899. /**
  900. * adds an ActionListener to the button
  901. */
  902. public void addActionListener(ActionListener l) {
  903. listenerList.add(ActionListener.class, l);
  904. }
  905. /**
  906. * removes an ActionListener from the button
  907. */
  908. public void removeActionListener(ActionListener l) {
  909. listenerList.remove(ActionListener.class, l);
  910. }
  911. /**
  912. * Subclasses that want to handle ChangeEvents differently
  913. * can override this to return another ChangeListener implementation.
  914. */
  915. protected ChangeListener createChangeListener() {
  916. return (ChangeListener) new ButtonChangeListener();
  917. }
  918. /**
  919. * Extend ChangeListener to be serializable
  920. * <p>
  921. * <strong>Warning:</strong>
  922. * Serialized objects of this class will not be compatible with
  923. * future Swing releases. The current serialization support is appropriate
  924. * for short term storage or RMI between applications running the same
  925. * version of Swing. A future release of Swing will provide support for
  926. * long term persistence.
  927. */
  928. protected class ButtonChangeListener implements ChangeListener, Serializable {
  929. ButtonChangeListener() {
  930. }
  931. public void stateChanged(ChangeEvent e) {
  932. fireStateChanged();
  933. repaint();
  934. }
  935. }
  936. /*
  937. * Notify all listeners that have registered interest for
  938. * notification on this event type. The event instance
  939. * is lazily created using the parameters passed into
  940. * the fire method.
  941. * @see EventListenerList
  942. */
  943. protected void fireActionPerformed(ActionEvent event) {
  944. // Guaranteed to return a non-null array
  945. Object[] listeners = listenerList.getListenerList();
  946. ActionEvent e = null;
  947. // Process the listeners last to first, notifying
  948. // those that are interested in this event
  949. for (int i = listeners.length-2; i>=0; i-=2) {
  950. if (listeners[i]==ActionListener.class) {
  951. // Lazily create the event:
  952. if (e == null) {
  953. String actionCommand = event.getActionCommand();
  954. if(actionCommand == null) {
  955. actionCommand = getActionCommand();
  956. }
  957. e = new ActionEvent(AbstractButton.this,
  958. ActionEvent.ACTION_PERFORMED,
  959. actionCommand,
  960. event.getModifiers());
  961. }
  962. ((ActionListener)listeners[i+1]).actionPerformed(e);
  963. }
  964. }
  965. }
  966. /*
  967. * Notify all listeners that have registered interest for
  968. * notification on this event type. The event instance
  969. * is lazily created using the parameters passed into
  970. * the fire method.
  971. * @see EventListenerList
  972. */
  973. protected void fireItemStateChanged(ItemEvent event) {
  974. // Guaranteed to return a non-null array
  975. Object[] listeners = listenerList.getListenerList();
  976. ItemEvent e = null;
  977. // Process the listeners last to first, notifying
  978. // those that are interested in this event
  979. for (int i = listeners.length-2; i>=0; i-=2) {
  980. if (listeners[i]==ItemListener.class) {
  981. // Lazily create the event:
  982. if (e == null) {
  983. e = new ItemEvent(AbstractButton.this,
  984. ItemEvent.ITEM_STATE_CHANGED,
  985. AbstractButton.this,
  986. event.getStateChange());
  987. }
  988. ((ItemListener)listeners[i+1]).itemStateChanged(e);
  989. }
  990. }
  991. }
  992. private class ForwardActionEvents implements ActionListener, Serializable {
  993. public void actionPerformed(ActionEvent event) {
  994. fireActionPerformed(event);
  995. }
  996. }
  997. protected ActionListener createActionListener() {
  998. return new ForwardActionEvents();
  999. }
  1000. private class ForwardItemEvents implements ItemListener, Serializable {
  1001. public void itemStateChanged(ItemEvent event) {
  1002. fireItemStateChanged(event);
  1003. }
  1004. }
  1005. protected ItemListener createItemListener() {
  1006. return new ForwardItemEvents();
  1007. }
  1008. /**
  1009. * Enables (or disables) the button.
  1010. */
  1011. public void setEnabled(boolean b) {
  1012. super.setEnabled(b);
  1013. model.setEnabled(b);
  1014. }
  1015. // *** Deprecated java.awt.Button APIs below *** //
  1016. /**
  1017. * Returns the label text.
  1018. *
  1019. * @return a String containing the label
  1020. * @deprecated - Replaced by getText()
  1021. */
  1022. public String getLabel() {
  1023. return getText();
  1024. }
  1025. /**
  1026. * Sets the label text.
  1027. *
  1028. * @param label a String containing the text
  1029. * @deprecated - Replaced by setText(text)
  1030. * @beaninfo
  1031. * bound: true
  1032. * description: Replace by setText(text)
  1033. */
  1034. public void setLabel(String label) {
  1035. setText(label);
  1036. }
  1037. /**
  1038. * adds an ItemListener to the checkbox
  1039. */
  1040. public void addItemListener(ItemListener l) {
  1041. listenerList.add(ItemListener.class, l);
  1042. }
  1043. /**
  1044. * removes an ItemListener from the button
  1045. */
  1046. public void removeItemListener(ItemListener l) {
  1047. listenerList.remove(ItemListener.class, l);
  1048. }
  1049. /**
  1050. * Returns an array (length 1) containing the label or null if the
  1051. * button is not selected.
  1052. *
  1053. * @return an array containing 1 Object -- the text of the button
  1054. * -- if the item is selected, otherwise null
  1055. */
  1056. public synchronized Object[] getSelectedObjects() {
  1057. if (isSelected() == false) {
  1058. return null;
  1059. }
  1060. Object[] selectedObjects = new Object[1];
  1061. selectedObjects[0] = getText();
  1062. return selectedObjects;
  1063. }
  1064. protected void init(String text, Icon icon) {
  1065. setLayout(new OverlayLayout(this));
  1066. if(text != null) {
  1067. setText(text);
  1068. }
  1069. if(icon != null) {
  1070. setIcon(icon);
  1071. }
  1072. // Set the UI
  1073. updateUI();
  1074. // Listen for Focus events
  1075. addFocusListener(
  1076. new FocusListener() {
  1077. public void focusGained(FocusEvent event) {
  1078. if (accessibleContext != null) {
  1079. accessibleContext.firePropertyChange(
  1080. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  1081. null, AccessibleState.FOCUSED);
  1082. }
  1083. }
  1084. public void focusLost(FocusEvent event) {
  1085. if (accessibleContext != null) {
  1086. accessibleContext.firePropertyChange(
  1087. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  1088. null, AccessibleState.FOCUSED);
  1089. }
  1090. // repaint focus is lost
  1091. if(isFocusPainted()) {
  1092. repaint();
  1093. }
  1094. }
  1095. }
  1096. );
  1097. setAlignmentX(LEFT_ALIGNMENT);
  1098. setAlignmentY(CENTER_ALIGNMENT);
  1099. }
  1100. /**
  1101. * Returns a string representation of this AbstractButton. This method
  1102. * is intended to be used only for debugging purposes, and the
  1103. * content and format of the returned string may vary between
  1104. * implementations. The returned string may be empty but may not
  1105. * be <code>null</code>.
  1106. * <P>
  1107. * Overriding paramString() to provide information about the
  1108. * specific new aspects of the JFC components.
  1109. *
  1110. * @return a string representation of this AbstractButton.
  1111. */
  1112. protected String paramString() {
  1113. String defaultIconString = (defaultIcon != null ?
  1114. defaultIcon.toString() : "");
  1115. String pressedIconString = (pressedIcon != null ?
  1116. pressedIcon.toString() : "");
  1117. String disabledIconString = (disabledIcon != null ?
  1118. disabledIcon.toString() : "");
  1119. String selectedIconString = (selectedIcon != null ?
  1120. selectedIcon.toString() : "");
  1121. String disabledSelectedIconString = (disabledSelectedIcon != null ?
  1122. disabledSelectedIcon.toString()
  1123. : "");
  1124. String rolloverIconString = (rolloverIcon != null ?
  1125. rolloverIcon.toString() : "");
  1126. String rolloverSelectedIconString = (rolloverSelectedIcon != null ?
  1127. rolloverSelectedIcon.toString()
  1128. : "");
  1129. String paintBorderString = (paintBorder ? "true" : "false");
  1130. String paintFocusString = (paintFocus ? "true" : "false");
  1131. String rolloverEnabledString = (rolloverEnabled ? "true" : "false");
  1132. return super.paramString() +
  1133. ",defaultIcon=" + defaultIconString +
  1134. ",disabledIcon=" + disabledIconString +
  1135. ",disabledSelectedIcon=" + disabledSelectedIconString +
  1136. ",margin=" + margin +
  1137. ",paintBorder=" + paintBorderString +
  1138. ",paintFocus=" + paintFocusString +
  1139. ",pressedIcon=" + pressedIconString +
  1140. ",rolloverEnabled=" + rolloverEnabledString +
  1141. ",rolloverIcon=" + rolloverIconString +
  1142. ",rolloverSelectedIcon=" + rolloverSelectedIconString +
  1143. ",selectedIcon=" + selectedIconString +
  1144. ",text=" + text;
  1145. }
  1146. ///////////////////
  1147. // Accessibility support
  1148. ///////////////////
  1149. /**
  1150. * Accessiblity support.
  1151. * <p>
  1152. * <strong>Warning:</strong>
  1153. * Serialized objects of this class will not be compatible with
  1154. * future Swing releases. The current serialization support is appropriate
  1155. * for short term storage or RMI between applications running the same
  1156. * version of Swing. A future release of Swing will provide support for
  1157. * long term persistence.
  1158. */
  1159. protected abstract class AccessibleAbstractButton
  1160. extends AccessibleJComponent implements AccessibleAction,
  1161. AccessibleValue {
  1162. /**
  1163. * Get the accessible name of this object.
  1164. *
  1165. * @return the localized name of the object -- can be null if this
  1166. * object does not have a name
  1167. */
  1168. public String getAccessibleName() {
  1169. if (accessibleName != null) {
  1170. return accessibleName;
  1171. } else {
  1172. if (getText() == null) {
  1173. return super.getAccessibleName();
  1174. } else {
  1175. return getText();
  1176. }
  1177. }
  1178. }
  1179. /**
  1180. * Get the state set of this object.
  1181. *
  1182. * @return an instance of AccessibleState containing the current state
  1183. * of the object
  1184. * @see AccessibleState
  1185. */
  1186. public AccessibleStateSet getAccessibleStateSet() {
  1187. AccessibleStateSet states = super.getAccessibleStateSet();
  1188. if (getModel().isArmed()) {
  1189. states.add(AccessibleState.ARMED);
  1190. }
  1191. if (hasFocus()) {
  1192. states.add(AccessibleState.FOCUSED);
  1193. }
  1194. if (getModel().isPressed()) {
  1195. states.add(AccessibleState.PRESSED);
  1196. }
  1197. if (isSelected()) {
  1198. states.add(AccessibleState.CHECKED);
  1199. }
  1200. return states;
  1201. }
  1202. /**
  1203. * Get the AccessibleAction associated with this object if one
  1204. * exists. Otherwise return null.
  1205. */
  1206. public AccessibleAction getAccessibleAction() {
  1207. return this;
  1208. }
  1209. /**
  1210. * Get the AccessibleValue associated with this object if one
  1211. * exists. Otherwise return null.
  1212. */
  1213. public AccessibleValue getAccessibleValue() {
  1214. return this;
  1215. }
  1216. /**
  1217. * Returns the number of Actions available in this object.
  1218. * If there is more than one, the first one is the "default"
  1219. * action.
  1220. *
  1221. * @return the number of Actions in this object
  1222. */
  1223. public int getAccessibleActionCount() {
  1224. return 1;
  1225. }
  1226. /**
  1227. * Return a description of the specified action of the object.
  1228. *
  1229. * @param i zero-based index of the actions
  1230. */
  1231. public String getAccessibleActionDescription(int i) {
  1232. if (i == 0) {
  1233. // [[[PENDING: WDW -- need to provide a localized string]]]
  1234. return new String("click");
  1235. } else {
  1236. return null;
  1237. }
  1238. }
  1239. /**
  1240. * Perform the specified Action on the object
  1241. *
  1242. * @param i zero-based index of actions
  1243. * @return true if the the action was performed; else false.
  1244. */
  1245. public boolean doAccessibleAction(int i) {
  1246. if (i == 0) {
  1247. doClick();
  1248. return true;
  1249. } else {
  1250. return false;
  1251. }
  1252. }
  1253. /**
  1254. * Get the value of this object as a Number.
  1255. *
  1256. * @return An Integer of 0 if this isn't selected or an Integer of 1 if
  1257. * this is selected.
  1258. * @see AbstractButton#isSelected
  1259. */
  1260. public Number getCurrentAccessibleValue() {
  1261. if (isSelected()) {
  1262. return new Integer(1);
  1263. } else {
  1264. return new Integer(0);
  1265. }
  1266. }
  1267. /**
  1268. * Set the value of this object as a Number.
  1269. *
  1270. * @return True if the value was set.
  1271. */
  1272. public boolean setCurrentAccessibleValue(Number n) {
  1273. if (n instanceof Integer) {
  1274. int i = n.intValue();
  1275. if (i == 0) {
  1276. setSelected(false);
  1277. } else {
  1278. setSelected(true);
  1279. }
  1280. return true;
  1281. } else {
  1282. return false;
  1283. }
  1284. }
  1285. /**
  1286. * Get the minimum value of this object as a Number.
  1287. *
  1288. * @return An Integer of 0.
  1289. */
  1290. public Number getMinimumAccessibleValue() {
  1291. return new Integer(0);
  1292. }
  1293. /**
  1294. * Get the maximum value of this object as a Number.
  1295. *
  1296. * @return An Integer of 1.
  1297. */
  1298. public Number getMaximumAccessibleValue() {
  1299. return new Integer(1);
  1300. }
  1301. }
  1302. }