1. /*
  2. * @(#)JTextField.java 1.59 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 javax.swing.text.*;
  11. import javax.swing.plaf.*;
  12. import javax.swing.event.*;
  13. import javax.accessibility.*;
  14. import java.io.ObjectOutputStream;
  15. import java.io.ObjectInputStream;
  16. import java.io.IOException;
  17. /**
  18. * JTextField is a lightweight component that allows the editing
  19. * of a single line of text. It is intended to be source-compatible
  20. * with java.awt.TextField where it is reasonable to do so. This
  21. * component has capabilities not found in the java.awt.TextField
  22. * class. The superclass should be consulted for additional capabilities.
  23. * <p>
  24. * JTextField has a method to establish the string used as the
  25. * command string for the action event that gets fired. The
  26. * java.awt.TextField used the text of the field as the command
  27. * string for the ActionEvent. JTextField will use the command
  28. * string set with the <code>setActionCommand</code> method if not null,
  29. * otherwise it will use the text of the field as a compatibility with
  30. * java.awt.TextField.
  31. * <p>
  32. * The method <code>setEchoChar</code> and <code>getEchoChar</code>
  33. * are not provided directly to avoid a new implementation of a
  34. * pluggable look-and-feel inadvertantly exposing password characters.
  35. * To provide password-like services a seperate class JPasswordField
  36. * extends JTextField to provide this service with an independantly
  37. * pluggable look-and-feel.
  38. * <p>
  39. * The java.awt.TextField could be monitored for changes by adding
  40. * a TextListener for TextEvent's. In the JTextComponent based
  41. * components, changes are broadcasted from the model via a
  42. * DocumentEvent to DocumentListeners. The DocumentEvent gives
  43. * the location of the change and the kind of change if desired.
  44. * The code fragment might look something like:
  45. * <pre><code>
  46. * DocumentListener myListener = ??;
  47. * JTextField myArea = ??;
  48. * myArea.getDocument().addDocumentListener(myListener);
  49. * </code></pre>
  50. * <p>
  51. * The horizontal alignment of JTextField can be set to be left
  52. * justified, centered, or right justified if the required size
  53. * of the field text is smaller than the size allocated to it.
  54. * This is determined by the <code>setHorizontalAlignment</code>
  55. * and <code>getHorizontalAlignment</code> methods. The default
  56. * is to be left justified.
  57. * <p>
  58. * For the keyboard keys used by this component in the standard Look and
  59. * Feel (L&F) renditions, see the
  60. * <a href="doc-files/Key-Index.html#JTextField">JTextField</a> key assignments.
  61. * <p>
  62. * For compatibility with java.awt.TextField, the VK_ENTER key fires
  63. * the ActionEvent to the registered ActionListeners. However, awt
  64. * didn't have default buttons like swing does. If a text field has
  65. * focus and the VK_ENTER key is pressed, it will fire the fields
  66. * ActionEvent rather than activate the default button. To disable
  67. * the compatibility with awt for text fields, the following code
  68. * fragment will remove the binding of VK_ENTER from the default keymap
  69. * used by all JTextFields if that is desired.
  70. * <pre><code>
  71. static {
  72. JTextField f = new JTextField();
  73. KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
  74. Keymap map = f.getKeymap();
  75. map.removeKeyStrokeBinding(enter);
  76. }
  77. * </code></pre>
  78. * <p>
  79. * Customized fields can easily be created by extending the model and
  80. * changing the default model provided. For example, the following piece
  81. * of code will create a field that holds only upper case characters. It
  82. * will work even if text is pasted into from the clipboard or it is altered via
  83. * programmatic changes.
  84. * <pre><code>
  85. public class UpperCaseField extends JTextField {
  86. public UpperCaseField(int cols) {
  87. super(cols);
  88. }
  89. protected Document createDefaultModel() {
  90. return new UpperCaseDocument();
  91. }
  92. static class UpperCaseDocument extends PlainDocument {
  93. public void insertString(int offs, String str, AttributeSet a)
  94. throws BadLocationException {
  95. if (str == null) {
  96. return;
  97. }
  98. char[] upper = str.toCharArray();
  99. for (int i = 0; i < upper.length; i++) {
  100. upper[i] = Character.toUpperCase(upper[i]);
  101. }
  102. super.insertString(offs, new String(upper), a);
  103. }
  104. }
  105. }
  106. * </code></pre>
  107. * <p>
  108. * <strong>Warning:</strong>
  109. * Serialized objects of this class will not be compatible with
  110. * future Swing releases. The current serialization support is appropriate
  111. * for short term storage or RMI between applications running the same
  112. * version of Swing. A future release of Swing will provide support for
  113. * long term persistence.
  114. *
  115. * @beaninfo
  116. * attribute: isContainer false
  117. *
  118. * @author Timothy Prinzing
  119. * @version 1.59 11/29/01
  120. * @see #setActionCommand
  121. * @see JPasswordField
  122. */
  123. public class JTextField extends JTextComponent implements SwingConstants {
  124. /**
  125. * Constructs a new TextField. A default model is created, the initial
  126. * string is null, and the number of columns is set to 0.
  127. */
  128. public JTextField() {
  129. this(null, null, 0);
  130. }
  131. /**
  132. * Constructs a new TextField initialized with the specified text.
  133. * A default model is created and the number of columns is 0.
  134. *
  135. * @param text the text to be displayed, or null
  136. */
  137. public JTextField(String text) {
  138. this(null, text, 0);
  139. }
  140. /**
  141. * Constructs a new empty TextField with the specified number of columns.
  142. * A default model is created and the initial string is set to null.
  143. *
  144. * @param columns the number of columns to use to calculate
  145. * the preferred width. If columns is set to zero, the
  146. * preferred width will be whatever naturally results from
  147. * the component implementation.
  148. */
  149. public JTextField(int columns) {
  150. this(null, null, columns);
  151. }
  152. /**
  153. * Constructs a new TextField initialized with the specified text
  154. * and columns. A default model is created.
  155. *
  156. * @param text the text to be displayed, or null
  157. * @param columns the number of columns to use to calculate
  158. * the preferred width. If columns is set to zero, the
  159. * preferred width will be whatever naturally results from
  160. * the component implementation.
  161. */
  162. public JTextField(String text, int columns) {
  163. this(null, text, columns);
  164. }
  165. /**
  166. * Constructs a new JTextField that uses the given text storage
  167. * model and the given number of columns. This is the constructor
  168. * through which the other constructors feed. If the document is null,
  169. * a default model is created.
  170. *
  171. * @param doc the text storage to use. If this is null, a default
  172. * will be provided by calling the createDefaultModel method.
  173. * @param text the initial string to display, or null
  174. * @param columns the number of columns to use to calculate
  175. * the preferred width >= 0. If columns is set to zero, the
  176. * preferred width will be whatever naturally results from
  177. * the component implementation.
  178. * @exception IllegalArgumentException if columns < 0
  179. */
  180. public JTextField(Document doc, String text, int columns) {
  181. if (columns < 0) {
  182. throw new IllegalArgumentException("columns less than zero.");
  183. }
  184. visibility = new DefaultBoundedRangeModel();
  185. visibility.addChangeListener(new ScrollRepainter());
  186. this.columns = columns;
  187. if (doc == null) {
  188. doc = createDefaultModel();
  189. }
  190. setDocument(doc);
  191. if (text != null) {
  192. setText(text);
  193. }
  194. }
  195. /**
  196. * Gets the class ID for a UI.
  197. *
  198. * @return the ID ("TextFieldUI")
  199. * @see JComponent#getUIClassID
  200. * @see UIDefaults#getUI
  201. */
  202. public String getUIClassID() {
  203. return uiClassID;
  204. }
  205. /**
  206. * Calls to revalidate that come from within the textfield itself will
  207. * be handled by validating the textfield.
  208. *
  209. * @see JComponent#revalidate
  210. * @see JComponent#isValidateRoot
  211. */
  212. public boolean isValidateRoot() {
  213. return true;
  214. }
  215. /**
  216. * Returns the horizontal alignment of the text.
  217. * Valid keys: JTextField.LEFT (the default), JTextField.CENTER,
  218. * JTextField.RIGHT.
  219. *
  220. * @return the alignment
  221. */
  222. public int getHorizontalAlignment() {
  223. return horizontalAlignment;
  224. }
  225. /**
  226. * Sets the horizontal alignment of the text.
  227. * Valid keys: JTextField.LEFT (the default), JTextField.CENTER,
  228. * JTextField.RIGHT. invalidate() and repaint() are called when the
  229. * alignment is set, and a PropertyChange event ("horizontalAlignment")
  230. * is fired.
  231. *
  232. * @param alignment the alignment
  233. * @exception IllegalArgumentException if the alignment
  234. * specified is not a valid key.
  235. * @beaninfo
  236. * preferred: true
  237. * bound: true
  238. * description: Set the field alignment to LEFT (the default), CENTER, RIGHT
  239. * enum: LEFT JTextField.LEFT CENTER JTextField.CENTER RIGHT JTextField.RIGHT
  240. */
  241. public void setHorizontalAlignment(int alignment) {
  242. if (alignment == horizontalAlignment) return;
  243. int oldValue = horizontalAlignment;
  244. if ((alignment == LEFT) || (alignment == CENTER) || (alignment == RIGHT)) {
  245. horizontalAlignment = alignment;
  246. } else {
  247. throw new IllegalArgumentException("horizontalAlignment");
  248. }
  249. firePropertyChange("horizontalAlignment", oldValue, horizontalAlignment);
  250. invalidate();
  251. repaint();
  252. }
  253. /**
  254. * Creates the default implementation of the model
  255. * to be used at construction if one isn't explicitly
  256. * given. An instance of PlainDocument is returned.
  257. *
  258. * @return the default model implementation
  259. */
  260. protected Document createDefaultModel() {
  261. return new PlainDocument();
  262. }
  263. /**
  264. * Returns the number of columns in this TextField.
  265. *
  266. * @return the number of columns >= 0
  267. */
  268. public int getColumns() {
  269. return columns;
  270. }
  271. /**
  272. * Sets the number of columns in this TextField, and then invalidate
  273. * the layout.
  274. *
  275. * @param columns the number of columns >= 0
  276. * @exception IllegalArgumentException if columns is less than 0
  277. * @beaninfo
  278. * description: the number of columns preferred for display
  279. */
  280. public void setColumns(int columns) {
  281. int oldVal = this.columns;
  282. if (columns < 0) {
  283. throw new IllegalArgumentException("columns less than zero.");
  284. }
  285. if (columns != oldVal) {
  286. this.columns = columns;
  287. invalidate();
  288. }
  289. }
  290. /**
  291. * Gets the column width.
  292. * The meaning of what a column is can be considered a fairly weak
  293. * notion for some fonts. This method is used to define the width
  294. * of a column. By default this is defined to be the width of the
  295. * character <em>m</em> for the font used. This method can be
  296. * redefined to be some alternative amount
  297. *
  298. * @return the column width >= 1
  299. */
  300. protected int getColumnWidth() {
  301. if (columnWidth == 0) {
  302. FontMetrics metrics = getFontMetrics(getFont());
  303. columnWidth = metrics.charWidth('m');
  304. }
  305. return columnWidth;
  306. }
  307. /**
  308. * Returns the preferred size Dimensions needed for this
  309. * TextField. If a non-zero number of columns has been
  310. * set, the width is set to the columns multiplied by
  311. * the column width.
  312. *
  313. * @return the dimensions
  314. */
  315. public Dimension getPreferredSize() {
  316. synchronized (getTreeLock()) {
  317. Dimension size = super.getPreferredSize();
  318. if (columns != 0) {
  319. size.width = columns * getColumnWidth();
  320. }
  321. return size;
  322. }
  323. }
  324. /**
  325. * Sets the current font. This removes cached row height and column
  326. * width so the new font will be reflected. revalidate() is called
  327. * after setting the font.
  328. *
  329. * @param f the new font
  330. */
  331. public void setFont(Font f) {
  332. super.setFont(f);
  333. columnWidth = 0;
  334. }
  335. /**
  336. * Adds the specified action listener to receive
  337. * action events from this textfield.
  338. *
  339. * @param l the action listener
  340. */
  341. public synchronized void addActionListener(ActionListener l) {
  342. listenerList.add(ActionListener.class, l);
  343. }
  344. /**
  345. * Removes the specified action listener so that it no longer
  346. * receives action events from this textfield.
  347. *
  348. * @param l the action listener
  349. */
  350. public synchronized void removeActionListener(ActionListener l) {
  351. listenerList.remove(ActionListener.class, l);
  352. }
  353. /**
  354. * Notifies all listeners that have registered interest for
  355. * notification on this event type. The event instance
  356. * is lazily created using the parameters passed into
  357. * the fire method. The listener list is processed in last to
  358. * first order.
  359. * @see EventListenerList
  360. */
  361. protected void fireActionPerformed() {
  362. // Guaranteed to return a non-null array
  363. Object[] listeners = listenerList.getListenerList();
  364. ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
  365. (command != null) ? command : getText());
  366. // Process the listeners last to first, notifying
  367. // those that are interested in this event
  368. for (int i = listeners.length-2; i>=0; i-=2) {
  369. if (listeners[i]==ActionListener.class) {
  370. ((ActionListener)listeners[i+1]).actionPerformed(e);
  371. }
  372. }
  373. }
  374. /**
  375. * Sets the command string used for action events.
  376. *
  377. * @param command the command string
  378. */
  379. public void setActionCommand(String command) {
  380. this.command = command;
  381. }
  382. /**
  383. * Fetches the command list for the editor. This is
  384. * the list of commands supported by the plugged-in UI
  385. * augmented by the collection of commands that the
  386. * editor itself supports. These are useful for binding
  387. * to events, such as in a keymap.
  388. *
  389. * @return the command list
  390. */
  391. public Action[] getActions() {
  392. return TextAction.augmentList(super.getActions(), defaultActions);
  393. }
  394. /**
  395. * Processes action events occurring on this textfield by
  396. * dispatching them to any registered ActionListener objects.
  397. * This is normally called by the controller registered with
  398. * textfield.
  399. */
  400. public void postActionEvent() {
  401. fireActionPerformed();
  402. }
  403. // --- Scrolling support -----------------------------------
  404. /**
  405. * Gets the visibility of the text field. This can
  406. * be adjusted to change the location of the visible
  407. * area if the size of the field is greater than
  408. * the area that was allocated to the field.
  409. *
  410. * The fields look-and-feel implementation manages
  411. * the values of the minimum, maximum, and extent
  412. * properties on the BoundedRangeModel.
  413. *
  414. * @return the visibility
  415. * @see BoundedRangeModel
  416. */
  417. public BoundedRangeModel getHorizontalVisibility() {
  418. return visibility;
  419. }
  420. /**
  421. * Gets the scroll offset.
  422. *
  423. * @return the offset >= 0
  424. */
  425. public int getScrollOffset() {
  426. return visibility.getValue();
  427. }
  428. /**
  429. * Sets the scroll offset.
  430. *
  431. * @param scrollOffset the offset >= 0
  432. */
  433. public void setScrollOffset(int scrollOffset) {
  434. visibility.setValue(scrollOffset);
  435. }
  436. /**
  437. * Scrolls the field left or right.
  438. *
  439. * @param r the region to scroll
  440. */
  441. public void scrollRectToVisible(Rectangle r) {
  442. // convert to coordinate system of the bounded range
  443. int x = r.x + visibility.getValue();
  444. if (x < visibility.getValue()) {
  445. // Scroll to the left
  446. visibility.setValue(x - 2);
  447. } else if(x > visibility.getValue() + visibility.getExtent()) {
  448. // Scroll to the right
  449. visibility.setValue(x - visibility.getExtent() + 2);
  450. }
  451. }
  452. // --- variables -------------------------------------------
  453. /**
  454. * Name of the action to send notification that the
  455. * contents of the field have been accepted. Typically
  456. * this is bound to a carriage-return.
  457. */
  458. public static final String notifyAction = "notify-field-accept";
  459. private BoundedRangeModel visibility;
  460. private int horizontalAlignment = LEFT;
  461. private int columns;
  462. private int columnWidth;
  463. private String command;
  464. private static final Action[] defaultActions = {
  465. new NotifyAction()
  466. };
  467. /**
  468. * @see #getUIClassID
  469. * @see #readObject
  470. */
  471. private static final String uiClassID = "TextFieldUI";
  472. // --- Action implementations -----------------------------------
  473. static class NotifyAction extends TextAction {
  474. NotifyAction() {
  475. super(notifyAction);
  476. }
  477. public void actionPerformed(ActionEvent e) {
  478. JTextComponent target = getFocusedComponent();
  479. if (target instanceof JTextField) {
  480. JTextField field = (JTextField) target;
  481. field.postActionEvent();
  482. }
  483. }
  484. }
  485. class ScrollRepainter implements ChangeListener {
  486. public void stateChanged(ChangeEvent e) {
  487. repaint();
  488. }
  489. }
  490. /**
  491. * See readObject() and writeObject() in JComponent for more
  492. * information about serialization in Swing.
  493. */
  494. private void writeObject(ObjectOutputStream s) throws IOException {
  495. s.defaultWriteObject();
  496. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  497. ui.installUI(this);
  498. }
  499. }
  500. /**
  501. * Returns a string representation of this JTextField. This method
  502. * is intended to be used only for debugging purposes, and the
  503. * content and format of the returned string may vary between
  504. * implementations. The returned string may be empty but may not
  505. * be <code>null</code>.
  506. *
  507. * @return a string representation of this JTextField.
  508. */
  509. protected String paramString() {
  510. String horizontalAlignmentString;
  511. if (horizontalAlignment == LEFT) {
  512. horizontalAlignmentString = "LEFT";
  513. } else if (horizontalAlignment == CENTER) {
  514. horizontalAlignmentString = "CENTER";
  515. } else if (horizontalAlignment == RIGHT) {
  516. horizontalAlignmentString = "RIGHT";
  517. } else horizontalAlignmentString = "";
  518. String commandString = (command != null ?
  519. command : "");
  520. return super.paramString() +
  521. ",columns=" + columns +
  522. ",columnWidth=" + columnWidth +
  523. ",command=" + commandString +
  524. ",horizontalAlignment=" + horizontalAlignmentString;
  525. }
  526. /////////////////
  527. // Accessibility support
  528. ////////////////
  529. /**
  530. * Get the AccessibleContext associated with this JTextField.
  531. * Creates a new context if necessary.
  532. *
  533. * @return the AccessibleContext of this JTextField
  534. */
  535. public AccessibleContext getAccessibleContext() {
  536. if (accessibleContext == null) {
  537. accessibleContext = new AccessibleJTextField();
  538. }
  539. return accessibleContext;
  540. }
  541. /**
  542. * The class used to obtain the accessible role for this object.
  543. * <p>
  544. * <strong>Warning:</strong>
  545. * Serialized objects of this class will not be compatible with
  546. * future Swing releases. The current serialization support is appropriate
  547. * for short term storage or RMI between applications running the same
  548. * version of Swing. A future release of Swing will provide support for
  549. * long term persistence.
  550. */
  551. protected class AccessibleJTextField extends AccessibleJTextComponent {
  552. /**
  553. * Gets the state set of this object.
  554. *
  555. * @return an instance of AccessibleStateSet describing the states
  556. * of the object
  557. * @see AccessibleState
  558. */
  559. public AccessibleStateSet getAccessibleStateSet() {
  560. AccessibleStateSet states = super.getAccessibleStateSet();
  561. states.add(AccessibleState.SINGLE_LINE);
  562. return states;
  563. }
  564. }
  565. }