1. /*
  2. * @(#)InputMethodEvent.java 1.20 03/01/23
  3. *
  4. * Copyright 2003 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.20 01/23/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. *
  86. * @param source the object where the event originated
  87. * @param id the event type
  88. * @param when a long integer that specifies the time the event occurred
  89. * @param text the combined committed and composed text,
  90. * committed text first; must be <code>null</code>
  91. * when the event type is <code>CARET_POSITION_CHANGED</code>
  92. * may be <code>null</code> for
  93. * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no
  94. * committed or composed text
  95. * @param committedCharacterCount the number of committed
  96. * characters in the text
  97. * @param caret the caret (a.k.a. insertion point);
  98. * <code>null</code> if there's no caret within current
  99. * composed text
  100. * @param visiblePosition the position that's most important
  101. * to be visible; <code>null</code> if there's no
  102. * recommendation for a visible position within current
  103. * composed text
  104. * @exception IllegalArgumentException if <code>id</code> is not
  105. * in the range
  106. * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>
  107. * or if id is <code>CARET_POSITION_CHANGED</code> and
  108. * <code>text</code> is not <code>null</code>
  109. * or if <code>committedCharacterCount</code> is not in the range
  110. * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code>
  111. *
  112. * @since 1.4
  113. */
  114. public InputMethodEvent(Component source, int id, long when,
  115. AttributedCharacterIterator text, int committedCharacterCount,
  116. TextHitInfo caret, TextHitInfo visiblePosition) {
  117. super(source, id);
  118. if (id < INPUT_METHOD_FIRST || id > INPUT_METHOD_LAST) {
  119. throw new IllegalArgumentException("id outside of valid range");
  120. }
  121. if (id == CARET_POSITION_CHANGED && text != null) {
  122. throw new IllegalArgumentException("text must be null for CARET_POSITION_CHANGED");
  123. }
  124. this.when = when;
  125. this.text = text;
  126. int textLength = 0;
  127. if (text != null) {
  128. textLength = text.getEndIndex() - text.getBeginIndex();
  129. }
  130. if (committedCharacterCount < 0 || committedCharacterCount > textLength) {
  131. throw new IllegalArgumentException("committedCharacterCount outside of valid range");
  132. }
  133. this.committedCharacterCount = committedCharacterCount;
  134. this.caret = caret;
  135. this.visiblePosition = visiblePosition;
  136. }
  137. /**
  138. * Constructs an <code>InputMethodEvent</code> with the specified
  139. * source component, type, text, caret, and visiblePosition.
  140. * <p>
  141. * The offsets of caret and visiblePosition are relative to the current
  142. * composed text; that is, the composed text within <code>text</code>
  143. * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
  144. * the composed text within the <code>text</code> of the
  145. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
  146. * The time stamp for this event is initialized by invoking
  147. * {@link java.awt.EventQueue#getMostRecentEventTime()}.
  148. *
  149. * @param source the object where the event originated
  150. * @param id the event type
  151. * @param text the combined committed and composed text,
  152. * committed text first; must be <code>null</code>
  153. * when the event type is <code>CARET_POSITION_CHANGED</code>
  154. * may be <code>null</code> for
  155. * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no
  156. * committed or composed text
  157. * @param committedCharacterCount the number of committed
  158. * characters in the text
  159. * @param caret the caret (a.k.a. insertion point);
  160. * <code>null</code> if there's no caret within current
  161. * composed text
  162. * @param visiblePosition the position that's most important
  163. * to be visible; <code>null</code> if there's no
  164. * recommendation for a visible position within current
  165. * composed text
  166. * @exception IllegalArgumentException if <code>id</code> is not
  167. * in the range
  168. * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>
  169. * or if id is <code>CARET_POSITION_CHANGED</code> and
  170. * <code>text</code> is not <code>null</code>
  171. * or if <code>committedCharacterCount</code> is not in the range
  172. * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code>
  173. */
  174. public InputMethodEvent(Component source, int id,
  175. AttributedCharacterIterator text, int committedCharacterCount,
  176. TextHitInfo caret, TextHitInfo visiblePosition) {
  177. this(source, id, EventQueue.getMostRecentEventTime(), text,
  178. committedCharacterCount, caret, visiblePosition);
  179. }
  180. /**
  181. * Constructs an <code>InputMethodEvent</code> with the
  182. * specified source component, type, caret, and visiblePosition.
  183. * The text is set to <code>null</code>,
  184. * <code>committedCharacterCount</code> to 0.
  185. * <p>
  186. * The offsets of <code>caret</code> and <code>visiblePosition</code>
  187. * are relative to the current composed text; that is,
  188. * the composed text within the <code>text</code> of the
  189. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event if the
  190. * event being constructed as a <code>CARET_POSITION_CHANGED</code> event.
  191. * For an <code>INPUT_METHOD_TEXT_CHANGED</code> event without text,
  192. * <code>caret</code> and <code>visiblePosition</code> must be
  193. * <code>null</code>.
  194. * The time stamp for this event is initialized by invoking
  195. * {@link java.awt.EventQueue#getMostRecentEventTime()}.
  196. *
  197. * @param source the object where the event originated
  198. * @param id the event type
  199. * @param caret the caret (a.k.a. insertion point);
  200. * <code>null</code> if there's no caret within current
  201. * composed text
  202. * @param visiblePosition the position that's most important
  203. * to be visible; <code>null</code> if there's no
  204. * recommendation for a visible position within current
  205. * composed text
  206. * @exception IllegalArgumentException if <code>id</code> is not
  207. * in the range
  208. * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>
  209. */
  210. public InputMethodEvent(Component source, int id, TextHitInfo caret,
  211. TextHitInfo visiblePosition) {
  212. this(source, id, EventQueue.getMostRecentEventTime(), null,
  213. 0, caret, visiblePosition);
  214. }
  215. /**
  216. * Gets the combined committed and composed text.
  217. * Characters from index 0 to index <code>getCommittedCharacterCount() - 1</code> are committed
  218. * text, the remaining characters are composed text.
  219. *
  220. * @return the text.
  221. * Always null for CARET_POSITION_CHANGED;
  222. * may be null for INPUT_METHOD_TEXT_CHANGED if there's no composed or committed text.
  223. */
  224. public AttributedCharacterIterator getText() {
  225. return text;
  226. }
  227. /**
  228. * Gets the number of committed characters in the text.
  229. */
  230. public int getCommittedCharacterCount() {
  231. return committedCharacterCount;
  232. }
  233. /**
  234. * Gets the caret.
  235. * <p>
  236. * The offset of the caret is relative to the current
  237. * composed text; that is, the composed text within getText()
  238. * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
  239. * the composed text within getText() of the
  240. * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
  241. *
  242. * @return the caret (a.k.a. insertion point).
  243. * Null if there's no caret within current composed text.
  244. */
  245. public TextHitInfo getCaret() {
  246. return caret;
  247. }
  248. /**
  249. * Gets the position that's most important to be visible.
  250. * <p>
  251. * The offset of the visible position 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 position that's most important to be visible.
  258. * Null if there's no recommendation for a visible position within current composed text.
  259. */
  260. public TextHitInfo getVisiblePosition() {
  261. return visiblePosition;
  262. }
  263. /**
  264. * Consumes this event so that it will not be processed
  265. * in the default manner by the source which originated it.
  266. */
  267. public void consume() {
  268. consumed = true;
  269. }
  270. /**
  271. * Returns whether or not this event has been consumed.
  272. * @see #consume
  273. */
  274. public boolean isConsumed() {
  275. return consumed;
  276. }
  277. /**
  278. * Returns the time stamp of when this event occurred.
  279. *
  280. * @return this event's timestamp
  281. * @since 1.4
  282. */
  283. public long getWhen() {
  284. return when;
  285. }
  286. /**
  287. * Returns a parameter string identifying this event.
  288. * This method is useful for event-logging and for debugging.
  289. * It contains the event ID in text form, the characters of the
  290. * committed and composed text
  291. * separated by "+", the number of committed characters,
  292. * the caret, and the visible position.
  293. *
  294. * @return a string identifying the event and its attributes
  295. */
  296. public String paramString() {
  297. String typeStr;
  298. switch(id) {
  299. case INPUT_METHOD_TEXT_CHANGED:
  300. typeStr = "INPUT_METHOD_TEXT_CHANGED";
  301. break;
  302. case CARET_POSITION_CHANGED:
  303. typeStr = "CARET_POSITION_CHANGED";
  304. break;
  305. default:
  306. typeStr = "unknown type";
  307. }
  308. String textString;
  309. if (text == null) {
  310. textString = "no text";
  311. } else {
  312. StringBuffer textBuffer = new StringBuffer("\"");
  313. int committedCharacterCount = this.committedCharacterCount;
  314. char c = text.first();
  315. while (committedCharacterCount-- > 0) {
  316. textBuffer.append(c);
  317. c = text.next();
  318. }
  319. textBuffer.append("\" + \"");
  320. while (c != CharacterIterator.DONE) {
  321. textBuffer.append(c);
  322. c = text.next();
  323. }
  324. textBuffer.append("\"");
  325. textString = textBuffer.toString();
  326. }
  327. String countString = committedCharacterCount + " characters committed";
  328. String caretString;
  329. if (caret == null) {
  330. caretString = "no caret";
  331. } else {
  332. caretString = "caret: " + caret.toString();
  333. }
  334. String visiblePositionString;
  335. if (visiblePosition == null) {
  336. visiblePositionString = "no visible position";
  337. } else {
  338. visiblePositionString = "visible position: " + visiblePosition.toString();
  339. }
  340. return typeStr + ", " + textString + ", " + countString + ", " + caretString + ", " + visiblePositionString;
  341. }
  342. /**
  343. * Initializes the <code>when</code> field if it is not present in the
  344. * object input stream. In that case, the field will be initialized by
  345. * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}.
  346. */
  347. private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
  348. s.defaultReadObject();
  349. if (when == 0) {
  350. when = EventQueue.getMostRecentEventTime();
  351. }
  352. }
  353. }