1. /*
  2. * @(#)TextComponent.java 1.52 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 java.awt;
  8. import java.awt.peer.TextComponentPeer;
  9. import java.awt.event.*;
  10. import java.io.ObjectOutputStream;
  11. import java.io.ObjectInputStream;
  12. import java.io.IOException;
  13. import sun.awt.SunToolkit;
  14. /**
  15. * The <code>TextComponent</code> class is the superclass of
  16. * any component that allows the editing of some text.
  17. * <p>
  18. * A text component embodies a string of text. The
  19. * <code>TextComponent</code> class defines a set of methods
  20. * that determine whether or not this text is editable. If the
  21. * component is editable, it defines another set of methods
  22. * that supports a text insertion caret.
  23. * <p>
  24. * In addition, the class defines methods that are used
  25. * to maintain a current <em>selection</em> from the text.
  26. * The text selection, a substring of the component's text,
  27. * is the target of editing operations. It is also referred
  28. * to as the <em>selected text</em>.
  29. *
  30. * @version 1.52, 11/29/01
  31. * @author Sami Shaio
  32. * @author Arthur van Hoff
  33. * @since JDK1.0
  34. */
  35. public class TextComponent extends Component {
  36. /**
  37. * The value of the text.
  38. * A null value is the same as "".
  39. *
  40. * @serial
  41. * @see setText()
  42. * @see getText()
  43. */
  44. String text;
  45. /**
  46. * A boolean indicating whether or not this TextComponent is editable.
  47. * It will be <code>true</code> if the text component
  48. * is editable and <code>false</code> if not.
  49. *
  50. * @serial
  51. * @see isEditable()
  52. */
  53. boolean editable = true;
  54. /**
  55. * The selection refers to the selected text, and the selectionStart
  56. * is the start position of the selected text.
  57. *
  58. * @serial
  59. * @see getSelectionStart()
  60. * @see setSelectionStart()
  61. */
  62. int selectionStart;
  63. /**
  64. * The selection refers to the selected text, and the selectionEnd
  65. * is the end position of the selected text.
  66. *
  67. * @serial
  68. * @see getSelectionEnd()
  69. * @see setSelectionEnd()
  70. */
  71. int selectionEnd;
  72. /**
  73. * true if this TextComponent has access to the System clipboard
  74. */
  75. transient private boolean canAccessClipboard;
  76. transient protected TextListener textListener;
  77. /*
  78. * JDK 1.1 serialVersionUID
  79. */
  80. private static final long serialVersionUID = -2214773872412987419L;
  81. /**
  82. * Constructs a new text component initialized with the
  83. * specified text. Sets the value of the cursor to
  84. * <code>Cursor.TEXT_CURSOR</code>.
  85. * @param text the text to be displayed. If
  86. * <code>text</code> is <code>null</code>, the empty
  87. * string <code>""</code> will be displayed.
  88. * @see java.awt.Cursor
  89. */
  90. TextComponent(String text) {
  91. this.text = (text != null) ? text : "";
  92. setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
  93. enableInputMethodsIfNecessary();
  94. checkSystemClipboardAccess();
  95. }
  96. private void enableInputMethodsIfNecessary() {
  97. try {
  98. Toolkit toolkit = Toolkit.getDefaultToolkit();
  99. enableInputMethods(((SunToolkit) toolkit).enableInputMethodsForTextComponent());
  100. } catch (Exception e) {
  101. // if something bad happens, just don't enable input methods
  102. }
  103. }
  104. boolean areInputMethodsEnabled() {
  105. // TextComponent handles key events without touching the eventMask or
  106. // having a key listener, so just check whether the flag is set
  107. return (eventMask & AWTEvent.INPUT_METHODS_ENABLED_MASK) != 0;
  108. }
  109. /**
  110. * Removes the TextComponent's peer. The peer allows us to modify
  111. * the appearance of the TextComponent without changing its
  112. * functionality.
  113. */
  114. public void removeNotify() {
  115. synchronized (getTreeLock()) {
  116. TextComponentPeer peer = (TextComponentPeer)this.peer;
  117. if (peer != null) {
  118. text = peer.getText();
  119. selectionStart = peer.getSelectionStart();
  120. selectionEnd = peer.getSelectionEnd();
  121. }
  122. super.removeNotify();
  123. }
  124. }
  125. /**
  126. * Sets the text that is presented by this
  127. * text component to be the specified text.
  128. * @param t the new text.
  129. * If this parameter is <code>null</code> then
  130. * the text is set to the empty string "".
  131. * @see java.awt.TextComponent#getText
  132. */
  133. public synchronized void setText(String t) {
  134. text = (t != null) ? t : "";
  135. TextComponentPeer peer = (TextComponentPeer)this.peer;
  136. if (peer != null) {
  137. peer.setText(text);
  138. }
  139. }
  140. /**
  141. * Gets the text that is presented by this text component.
  142. * @see java.awt.TextComponent#setText
  143. */
  144. public synchronized String getText() {
  145. TextComponentPeer peer = (TextComponentPeer)this.peer;
  146. if (peer != null) {
  147. text = peer.getText();
  148. }
  149. return text;
  150. }
  151. /**
  152. * Gets the selected text from the text that is
  153. * presented by this text component.
  154. * @return the selected text of this text component.
  155. * @see java.awt.TextComponent#select
  156. */
  157. public synchronized String getSelectedText() {
  158. return getText().substring(getSelectionStart(), getSelectionEnd());
  159. }
  160. /**
  161. * Indicates whether or not this text component is editable.
  162. * @return <code>true</code> if this text component is
  163. * editable; <code>false</code> otherwise.
  164. * @see java.awt.TextComponent#setEditable
  165. * @since JDK1ble
  166. */
  167. public boolean isEditable() {
  168. return editable;
  169. }
  170. /**
  171. * Sets the flag that determines whether or not this
  172. * text component is editable.
  173. * <p>
  174. * If the flag is set to <code>true</code>, this text component
  175. * becomes user editable. If the flag is set to <code>false</code>,
  176. * the user cannot change the text of this text component.
  177. * @param b a flag indicating whether this text component
  178. * is user editable.
  179. * @see java.awt.TextComponent#isEditable
  180. */
  181. public synchronized void setEditable(boolean b) {
  182. editable = b;
  183. TextComponentPeer peer = (TextComponentPeer)this.peer;
  184. if (peer != null) {
  185. peer.setEditable(b);
  186. }
  187. }
  188. /**
  189. * Gets the start position of the selected text in
  190. * this text component.
  191. * @return the start position of the selected text.
  192. * @see java.awt.TextComponent#setSelectionStart
  193. * @see java.awt.TextComponent#getSelectionEnd
  194. */
  195. public synchronized int getSelectionStart() {
  196. TextComponentPeer peer = (TextComponentPeer)this.peer;
  197. if (peer != null) {
  198. selectionStart = peer.getSelectionStart();
  199. }
  200. return selectionStart;
  201. }
  202. /**
  203. * Sets the selection start for this text component to
  204. * the specified position. The new start point is constrained
  205. * to be at or before the current selection end. It also
  206. * cannot be set to less than zero, the beginning of the
  207. * component's text.
  208. * If the caller supplies a value for <code>selectionStart</code>
  209. * that is out of bounds, the method enforces these constraints
  210. * silently, and without failure.
  211. * @param selectionStart the start position of the
  212. * selected text.
  213. * @see java.awt.TextComponent#getSelectionStart
  214. * @see java.awt.TextComponent#setSelectionEnd
  215. * @since JDK1.1
  216. */
  217. public synchronized void setSelectionStart(int selectionStart) {
  218. /* Route through select method to enforce consistent policy
  219. * between selectionStart and selectionEnd.
  220. */
  221. select(selectionStart, getSelectionEnd());
  222. }
  223. /**
  224. * Gets the end position of the selected text in
  225. * this text component.
  226. * @return the end position of the selected text.
  227. * @see java.awt.TextComponent#setSelectionEnd
  228. * @see java.awt.TextComponent#getSelectionStart
  229. */
  230. public synchronized int getSelectionEnd() {
  231. TextComponentPeer peer = (TextComponentPeer)this.peer;
  232. if (peer != null) {
  233. selectionEnd = peer.getSelectionEnd();
  234. }
  235. return selectionEnd;
  236. }
  237. /**
  238. * Sets the selection end for this text component to
  239. * the specified position. The new end point is constrained
  240. * to be at or after the current selection start. It also
  241. * cannot be set beyond the end of the component's text.
  242. * If the caller supplies a value for <code>selectionEnd</code>
  243. * that is out of bounds, the method enforces these constraints
  244. * silently, and without failure.
  245. * @param selectionEnd the end position of the
  246. * selected text.
  247. * @see java.awt.TextComponent#getSelectionEnd
  248. * @see java.awt.TextComponent#setSelectionStart
  249. * @since JDK1.1
  250. */
  251. public synchronized void setSelectionEnd(int selectionEnd) {
  252. /* Route through select method to enforce consistent policy
  253. * between selectionStart and selectionEnd.
  254. */
  255. select(getSelectionStart(), selectionEnd);
  256. }
  257. /**
  258. * Selects the text between the specified start and end positions.
  259. * <p>
  260. * This method sets the start and end positions of the
  261. * selected text, enforcing the restriction that the start position
  262. * must be greater than or equal to zero. The end position must be
  263. * greater than or equal to the start position, and less than or
  264. * equal to the length of the text component's text. The
  265. * character positions are indexed starting with zero.
  266. * The length of the selection is endPosition-startPosition, so the
  267. * character at endPosition is not selected.
  268. * If the start and end positions of the selected text are equal,
  269. * all text is deselected.
  270. * <p>
  271. * If the caller supplies values that are inconsistent or out of
  272. * bounds, the method enforces these constraints silently, and
  273. * without failure. Specifically, if the start position or end
  274. * position is greater than the length of the text, it is reset to
  275. * equal the text length. If the start position is less than zero,
  276. * it is reset to zero, and if the end position is less than the
  277. * start position, it is reset to the start position.
  278. *
  279. * @param selectionStart the zero-based index of the first
  280. character to be selected.
  281. * @param selectionEnd the zero-based end position of the
  282. text to be selected. The character at
  283. selectionEnd is not selected.
  284. * @see java.awt.TextComponent#setSelectionStart
  285. * @see java.awt.TextComponent#setSelectionEnd
  286. * @see java.awt.TextComponent#selectAll
  287. */
  288. public synchronized void select(int selectionStart, int selectionEnd) {
  289. String text = getText();
  290. if (selectionStart < 0) {
  291. selectionStart = 0;
  292. }
  293. if (selectionStart > text.length()) {
  294. selectionStart = text.length();
  295. }
  296. if (selectionEnd > text.length()) {
  297. selectionEnd = text.length();
  298. }
  299. if (selectionEnd < selectionStart) {
  300. selectionEnd = selectionStart;
  301. }
  302. this.selectionStart = selectionStart;
  303. this.selectionEnd = selectionEnd;
  304. TextComponentPeer peer = (TextComponentPeer)this.peer;
  305. if (peer != null) {
  306. peer.select(selectionStart, selectionEnd);
  307. }
  308. }
  309. /**
  310. * Selects all the text in this text component.
  311. * @see java.awt.TextComponent@select
  312. */
  313. public synchronized void selectAll() {
  314. String text = getText();
  315. this.selectionStart = 0;
  316. this.selectionEnd = getText().length();
  317. TextComponentPeer peer = (TextComponentPeer)this.peer;
  318. if (peer != null) {
  319. peer.select(selectionStart, selectionEnd);
  320. }
  321. }
  322. /**
  323. * Sets the position of the text insertion caret for
  324. * this text component.
  325. *
  326. * @param position the position of the text insertion caret.
  327. * @exception IllegalArgumentException if the value supplied
  328. * for <code>position</code> is less than zero.
  329. * @since JDK1.1
  330. */
  331. public synchronized void setCaretPosition(int position) {
  332. if (position < 0) {
  333. throw new IllegalArgumentException("position less than zero.");
  334. }
  335. int maxposition = getText().length();
  336. if (position > maxposition) {
  337. position = maxposition;
  338. }
  339. TextComponentPeer peer = (TextComponentPeer)this.peer;
  340. if (peer != null) {
  341. peer.setCaretPosition(position);
  342. } else {
  343. throw new IllegalComponentStateException("Cannot set caret position until after the peer has been created");
  344. }
  345. }
  346. /**
  347. * Gets the position of the text insertion caret for
  348. * this text component.
  349. * @return the position of the text insertion caret.
  350. * @since JDK1.1
  351. */
  352. public synchronized int getCaretPosition() {
  353. TextComponentPeer peer = (TextComponentPeer)this.peer;
  354. int position = 0;
  355. if (peer != null) {
  356. position = peer.getCaretPosition();
  357. }
  358. return position;
  359. }
  360. /**
  361. * Adds the specified text event listener to receive text events
  362. * from this text component.
  363. * If l is null, no exception is thrown and no action is performed.
  364. *
  365. * @param l the text event listener
  366. */
  367. public synchronized void addTextListener(TextListener l) {
  368. if (l == null) {
  369. return;
  370. }
  371. textListener = AWTEventMulticaster.add(textListener, l);
  372. newEventsOnly = true;
  373. }
  374. /**
  375. * Removes the specified text event listener so that it no longer
  376. * receives text events from this text component
  377. * If l is null, no exception is thrown and no action is performed.
  378. *
  379. * @param l the text listener.
  380. * @see java.awt.event.TextListener
  381. * @see java.awt.Button#addTextListener
  382. * @since JDK1.1
  383. */
  384. public synchronized void removeTextListener(TextListener l) {
  385. if (l == null) {
  386. return;
  387. }
  388. textListener = AWTEventMulticaster.remove(textListener, l);
  389. }
  390. // REMIND: remove when filtering is done at lower level
  391. boolean eventEnabled(AWTEvent e) {
  392. if (e.id == TextEvent.TEXT_VALUE_CHANGED) {
  393. if ((eventMask & AWTEvent.TEXT_EVENT_MASK) != 0 ||
  394. textListener != null) {
  395. return true;
  396. }
  397. return false;
  398. }
  399. return super.eventEnabled(e);
  400. }
  401. /**
  402. * Processes events on this text component. If the event is a
  403. * TextEvent, it invokes the processTextEvent method,
  404. * else it invokes its superclass's processEvent.
  405. * @param e the event
  406. */
  407. protected void processEvent(AWTEvent e) {
  408. if (e instanceof TextEvent) {
  409. processTextEvent((TextEvent)e);
  410. return;
  411. }
  412. super.processEvent(e);
  413. }
  414. /**
  415. * Processes text events occurring on this text component by
  416. * dispatching them to any registered TextListener objects.
  417. * NOTE: This method will not be called unless text events
  418. * are enabled for this component; this happens when one of the
  419. * following occurs:
  420. * a) A TextListener object is registered via addTextListener()
  421. * b) Text events are enabled via enableEvents()
  422. * @see Component#enableEvents
  423. * @param e the text event
  424. */
  425. protected void processTextEvent(TextEvent e) {
  426. if (textListener != null) {
  427. int id = e.getID();
  428. switch (id) {
  429. case TextEvent.TEXT_VALUE_CHANGED:
  430. textListener.textValueChanged(e);
  431. break;
  432. }
  433. }
  434. }
  435. /**
  436. * Returns the parameter string representing the state of this text
  437. * component. This string is useful for debugging.
  438. * @return the parameter string of this text component.
  439. */
  440. protected String paramString() {
  441. String str = super.paramString() + ",text=" + getText();
  442. if (editable) {
  443. str += ",editable";
  444. }
  445. return str + ",selection=" + getSelectionStart() + "-" + getSelectionEnd();
  446. }
  447. /**
  448. * Assigns a valid value to the canAccessClipboard instance variable.
  449. */
  450. private void checkSystemClipboardAccess() {
  451. canAccessClipboard = true;
  452. SecurityManager sm = System.getSecurityManager();
  453. if (sm != null) {
  454. try {
  455. sm.checkSystemClipboardAccess();
  456. }
  457. catch (SecurityException e) {
  458. canAccessClipboard = false;
  459. }
  460. }
  461. }
  462. /*
  463. * Serialization support.
  464. */
  465. /**
  466. * The textComponent SerializedDataVersion.
  467. *
  468. * @serial
  469. */
  470. private int textComponentSerializedDataVersion = 1;
  471. /**
  472. * Writes default serializable fields to stream. Writes
  473. * a list of serializable TextListener(s) as optional data.
  474. * The non-serializable TextListener(s) are detected and
  475. * no attempt is made to serialize them.
  476. *
  477. * @serialData Null terminated sequence of zero or more pairs.
  478. * A pair consists of a String and Object.
  479. * The String indicates the type of object and
  480. * is one of the following :
  481. * textListenerK indicating and TextListener object.
  482. *
  483. * @see AWTEventMulticaster.save(ObjectOutputStream, String, EventListener)
  484. * @see java.awt.Component.textListenerK
  485. */
  486. private void writeObject(java.io.ObjectOutputStream s)
  487. throws IOException
  488. {
  489. // Serialization support. Since the value of the fields
  490. // selectionStart, selectionEnd, and text aren't necessarily
  491. // up to date, we sync them up with the peer before serializing.
  492. TextComponentPeer peer = (TextComponentPeer)this.peer;
  493. if (peer != null) {
  494. text = peer.getText();
  495. selectionStart = peer.getSelectionStart();
  496. selectionEnd = peer.getSelectionEnd();
  497. }
  498. s.defaultWriteObject();
  499. AWTEventMulticaster.save(s, textListenerK, textListener);
  500. s.writeObject(null);
  501. }
  502. /**
  503. * Read the ObjectInputStream, and if it isn't null,
  504. * add a listener to receive text events fired by the
  505. * TextComponent. Unrecognized keys or values will be
  506. * ignored.
  507. *
  508. * @see removeTextListener()
  509. * @see addTextListener()
  510. */
  511. private void readObject(ObjectInputStream s)
  512. throws ClassNotFoundException, IOException
  513. {
  514. s.defaultReadObject();
  515. // Make sure the state we just read in for text,
  516. // selectionStart and selectionEnd has legal values
  517. this.text = (text != null) ? text : "";
  518. select(selectionStart, selectionEnd);
  519. Object keyOrNull;
  520. while(null != (keyOrNull = s.readObject())) {
  521. String key = ((String)keyOrNull).intern();
  522. if (textListenerK == key) {
  523. addTextListener((TextListener)(s.readObject()));
  524. } else {
  525. // skip value for unrecognized key
  526. s.readObject();
  527. }
  528. }
  529. enableInputMethodsIfNecessary();
  530. checkSystemClipboardAccess();
  531. }
  532. }