1. /*
  2. * @(#)InputMethodEvent.java 1.22 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt.event;
  8. import java.awt.AWTEvent;
  9. import java.awt.Component;
  10. import java.awt.EventQueue;
  11. import java.awt.font.TextHitInfo;
  12. import java.io.IOException;
  13. import java.io.ObjectInputStream;
  14. import java.lang.Integer;
  15. import java.text.AttributedCharacterIterator;
  16. import java.text.CharacterIterator;
  17. /**
  18. * Input method events contain information about text that is being
  19. * composed using an input method. Whenever the text changes, the
  20. * input method sends an event. If the text component that's currently
  21. * using the input method is an active client, the event is dispatched
  22. * to that component. Otherwise, it is dispatched to a separate
  23. * composition window.
  24. *
  25. * <p>
  26. * The text included with the input method event consists of two parts:
  27. * committed text and composed text. Either part may be empty. The two
  28. * parts together replace any uncommitted composed text sent in previous events,
  29. * or the currently selected committed text.
  30. * Committed text should be integrated into the text component's persistent
  31. * data, it will not be sent again. Composed text may be sent repeatedly,
  32. * with changes to reflect the user's editing operations. Committed text
  33. * always precedes composed text.
  34. *
  35. * @author JavaSoft Asia/Pacific
  36. * @version 1.22 12/19/03
  37. * @since 1.2
  38. */
  39. public class InputMethodEvent extends AWTEvent {
  40. /**
  41. * Serial Version ID.
  42. */
  43. private static final long serialVersionUID = 4727190874778922661L;
  44. /**
  45. * Marks the first integer id for the range of input method event ids.
  46. */
  47. public static final int INPUT_METHOD_FIRST = 1100;
  48. /**
  49. * The event type indicating changed input method text. This event is
  50. * generated by input methods while processing input.
  51. */
  52. public static final int INPUT_METHOD_TEXT_CHANGED = INPUT_METHOD_FIRST;
  53. /**
  54. * The event type indicating a changed insertion point in input method text.
  55. * This event is
  56. * generated by input methods while processing input if only the caret changed.
  57. */
  58. public static final int CARET_POSITION_CHANGED = INPUT_METHOD_FIRST + 1;
  59. /**
  60. * Marks the last integer id for the range of input method event ids.
  61. */
  62. public static final int INPUT_METHOD_LAST = INPUT_METHOD_FIRST + 1;
  63. /**
  64. * The time stamp that indicates when the event was created.
  65. *
  66. * @serial
  67. * @see #getWhen
  68. * @since 1.4
  69. */
  70. long when;
  71. // Text object
  72. private transient AttributedCharacterIterator text;
  73. private transient int committedCharacterCount;
  74. private transient TextHitInfo caret;
  75. private transient TextHitInfo visiblePosition;
  76. /**
  77. * Constructs an <code>InputMethodEvent</code> with the specified
  78. * source component, type, time, text, caret, and visiblePosition.
  79. * <p>
  80. * The offsets of caret and visiblePosition are relative to the current
  81. * composed text; that is, the composed text within <code>text</code>
  82. * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
  83. * the composed text within the <code>text</code> of the
  84. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
  85. * <p>Note that passing in an invalid <code>id</code> results in
  86. * unspecified behavior. This method throws an
  87. * <code>IllegalArgumentException</code> if <code>source</code>
  88. * is <code>null</code>.
  89. *
  90. * @param source the object where the event originated
  91. * @param id the event type
  92. * @param when a long integer that specifies the time the event occurred
  93. * @param text the combined committed and composed text,
  94. * committed text first; must be <code>null</code>
  95. * when the event type is <code>CARET_POSITION_CHANGED</code>
  96. * may be <code>null</code> for
  97. * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no
  98. * committed or composed text
  99. * @param committedCharacterCount the number of committed
  100. * characters in the text
  101. * @param caret the caret (a.k.a. insertion point);
  102. * <code>null</code> if there's no caret within current
  103. * composed text
  104. * @param visiblePosition the position that's most important
  105. * to be visible; <code>null</code> if there's no
  106. * recommendation for a visible position within current
  107. * composed text
  108. * @throws IllegalArgumentException if <code>id</code> is not
  109. * in the range
  110. * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>
  111. * or if id is <code>CARET_POSITION_CHANGED</code> and
  112. * <code>text</code> is not <code>null</code>
  113. * or if <code>committedCharacterCount</code> is not in the range
  114. * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code>
  115. * @throws IllegalArgumentException if <code>source</code> is null
  116. *
  117. * @since 1.4
  118. */
  119. public InputMethodEvent(Component source, int id, long when,
  120. AttributedCharacterIterator text, int committedCharacterCount,
  121. TextHitInfo caret, TextHitInfo visiblePosition) {
  122. super(source, id);
  123. if (id < INPUT_METHOD_FIRST || id > INPUT_METHOD_LAST) {
  124. throw new IllegalArgumentException("id outside of valid range");
  125. }
  126. if (id == CARET_POSITION_CHANGED && text != null) {
  127. throw new IllegalArgumentException("text must be null for CARET_POSITION_CHANGED");
  128. }
  129. this.when = when;
  130. this.text = text;
  131. int textLength = 0;
  132. if (text != null) {
  133. textLength = text.getEndIndex() - text.getBeginIndex();
  134. }
  135. if (committedCharacterCount < 0 || committedCharacterCount > textLength) {
  136. throw new IllegalArgumentException("committedCharacterCount outside of valid range");
  137. }
  138. this.committedCharacterCount = committedCharacterCount;
  139. this.caret = caret;
  140. this.visiblePosition = visiblePosition;
  141. }
  142. /**
  143. * Constructs an <code>InputMethodEvent</code> with the specified
  144. * source component, type, text, caret, and visiblePosition.
  145. * <p>
  146. * The offsets of caret and visiblePosition are relative to the current
  147. * composed text; that is, the composed text within <code>text</code>
  148. * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
  149. * the composed text within the <code>text</code> of the
  150. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
  151. * The time stamp for this event is initialized by invoking
  152. * {@link java.awt.EventQueue#getMostRecentEventTime()}.
  153. * <p>Note that passing in an invalid <code>id</code> results in
  154. * unspecified behavior. This method throws an
  155. * <code>IllegalArgumentException</code> if <code>source</code>
  156. * is <code>null</code>.
  157. *
  158. * @param source the object where the event originated
  159. * @param id the event type
  160. * @param text the combined committed and composed text,
  161. * committed text first; must be <code>null</code>
  162. * when the event type is <code>CARET_POSITION_CHANGED</code>
  163. * may be <code>null</code> for
  164. * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no
  165. * committed or composed text
  166. * @param committedCharacterCount the number of committed
  167. * characters in the text
  168. * @param caret the caret (a.k.a. insertion point);
  169. * <code>null</code> if there's no caret within current
  170. * composed text
  171. * @param visiblePosition the position that's most important
  172. * to be visible; <code>null</code> if there's no
  173. * recommendation for a visible position within current
  174. * composed text
  175. * @throws IllegalArgumentException if <code>id</code> is not
  176. * in the range
  177. * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>
  178. * or if id is <code>CARET_POSITION_CHANGED</code> and
  179. * <code>text</code> is not <code>null</code>
  180. * or if <code>committedCharacterCount</code> is not in the range
  181. * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code>
  182. * @throws IllegalArgumentException if <code>source</code> is null
  183. */
  184. public InputMethodEvent(Component source, int id,
  185. AttributedCharacterIterator text, int committedCharacterCount,
  186. TextHitInfo caret, TextHitInfo visiblePosition) {
  187. this(source, id, EventQueue.getMostRecentEventTime(), text,
  188. committedCharacterCount, caret, visiblePosition);
  189. }
  190. /**
  191. * Constructs an <code>InputMethodEvent</code> with the
  192. * specified source component, type, caret, and visiblePosition.
  193. * The text is set to <code>null</code>,
  194. * <code>committedCharacterCount</code> to 0.
  195. * <p>
  196. * The offsets of <code>caret</code> and <code>visiblePosition</code>
  197. * are relative to the current composed text; that is,
  198. * the composed text within the <code>text</code> of the
  199. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event if the
  200. * event being constructed as a <code>CARET_POSITION_CHANGED</code> event.
  201. * For an <code>INPUT_METHOD_TEXT_CHANGED</code> event without text,
  202. * <code>caret</code> and <code>visiblePosition</code> must be
  203. * <code>null</code>.
  204. * The time stamp for this event is initialized by invoking
  205. * {@link java.awt.EventQueue#getMostRecentEventTime()}.
  206. * <p>Note that passing in an invalid <code>id</code> results in
  207. * unspecified behavior. This method throws an
  208. * <code>IllegalArgumentException</code> if <code>source</code>
  209. * is <code>null</code>.
  210. *
  211. * @param source the object where the event originated
  212. * @param id the event type
  213. * @param caret the caret (a.k.a. insertion point);
  214. * <code>null</code> if there's no caret within current
  215. * composed text
  216. * @param visiblePosition the position that's most important
  217. * to be visible; <code>null</code> if there's no
  218. * recommendation for a visible position within current
  219. * composed text
  220. * @throws IllegalArgumentException if <code>id</code> is not
  221. * in the range
  222. * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>
  223. * @throws IllegalArgumentException if <code>source</code> is null
  224. */
  225. public InputMethodEvent(Component source, int id, TextHitInfo caret,
  226. TextHitInfo visiblePosition) {
  227. this(source, id, EventQueue.getMostRecentEventTime(), null,
  228. 0, caret, visiblePosition);
  229. }
  230. /**
  231. * Gets the combined committed and composed text.
  232. * Characters from index 0 to index <code>getCommittedCharacterCount() - 1</code> are committed
  233. * text, the remaining characters are composed text.
  234. *
  235. * @return the text.
  236. * Always null for CARET_POSITION_CHANGED;
  237. * may be null for INPUT_METHOD_TEXT_CHANGED if there's no composed or committed text.
  238. */
  239. public AttributedCharacterIterator getText() {
  240. return text;
  241. }
  242. /**
  243. * Gets the number of committed characters in the text.
  244. */
  245. public int getCommittedCharacterCount() {
  246. return committedCharacterCount;
  247. }
  248. /**
  249. * Gets the caret.
  250. * <p>
  251. * The offset of the caret is relative to the current
  252. * composed text; that is, the composed text within getText()
  253. * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
  254. * the composed text within getText() of the
  255. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
  256. *
  257. * @return the caret (a.k.a. insertion point).
  258. * Null if there's no caret within current composed text.
  259. */
  260. public TextHitInfo getCaret() {
  261. return caret;
  262. }
  263. /**
  264. * Gets the position that's most important to be visible.
  265. * <p>
  266. * The offset of the visible position is relative to the current
  267. * composed text; that is, the composed text within getText()
  268. * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
  269. * the composed text within getText() of the
  270. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
  271. *
  272. * @return the position that's most important to be visible.
  273. * Null if there's no recommendation for a visible position within current composed text.
  274. */
  275. public TextHitInfo getVisiblePosition() {
  276. return visiblePosition;
  277. }
  278. /**
  279. * Consumes this event so that it will not be processed
  280. * in the default manner by the source which originated it.
  281. */
  282. public void consume() {
  283. consumed = true;
  284. }
  285. /**
  286. * Returns whether or not this event has been consumed.
  287. * @see #consume
  288. */
  289. public boolean isConsumed() {
  290. return consumed;
  291. }
  292. /**
  293. * Returns the time stamp of when this event occurred.
  294. *
  295. * @return this event's timestamp
  296. * @since 1.4
  297. */
  298. public long getWhen() {
  299. return when;
  300. }
  301. /**
  302. * Returns a parameter string identifying this event.
  303. * This method is useful for event-logging and for debugging.
  304. * It contains the event ID in text form, the characters of the
  305. * committed and composed text
  306. * separated by "+", the number of committed characters,
  307. * the caret, and the visible position.
  308. *
  309. * @return a string identifying the event and its attributes
  310. */
  311. public String paramString() {
  312. String typeStr;
  313. switch(id) {
  314. case INPUT_METHOD_TEXT_CHANGED:
  315. typeStr = "INPUT_METHOD_TEXT_CHANGED";
  316. break;
  317. case CARET_POSITION_CHANGED:
  318. typeStr = "CARET_POSITION_CHANGED";
  319. break;
  320. default:
  321. typeStr = "unknown type";
  322. }
  323. String textString;
  324. if (text == null) {
  325. textString = "no text";
  326. } else {
  327. StringBuffer textBuffer = new StringBuffer("\"");
  328. int committedCharacterCount = this.committedCharacterCount;
  329. char c = text.first();
  330. while (committedCharacterCount-- > 0) {
  331. textBuffer.append(c);
  332. c = text.next();
  333. }
  334. textBuffer.append("\" + \"");
  335. while (c != CharacterIterator.DONE) {
  336. textBuffer.append(c);
  337. c = text.next();
  338. }
  339. textBuffer.append("\"");
  340. textString = textBuffer.toString();
  341. }
  342. String countString = committedCharacterCount + " characters committed";
  343. String caretString;
  344. if (caret == null) {
  345. caretString = "no caret";
  346. } else {
  347. caretString = "caret: " + caret.toString();
  348. }
  349. String visiblePositionString;
  350. if (visiblePosition == null) {
  351. visiblePositionString = "no visible position";
  352. } else {
  353. visiblePositionString = "visible position: " + visiblePosition.toString();
  354. }
  355. return typeStr + ", " + textString + ", " + countString + ", " + caretString + ", " + visiblePositionString;
  356. }
  357. /**
  358. * Initializes the <code>when</code> field if it is not present in the
  359. * object input stream. In that case, the field will be initialized by
  360. * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}.
  361. */
  362. private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
  363. s.defaultReadObject();
  364. if (when == 0) {
  365. when = EventQueue.getMostRecentEventTime();
  366. }
  367. }
  368. }