001 /*
002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt.event;
027
028 import java.awt.AWTEvent;
029 import java.awt.Component;
030 import java.awt.EventQueue;
031 import java.awt.font.TextHitInfo;
032 import java.io.IOException;
033 import java.io.ObjectInputStream;
034 import java.text.AttributedCharacterIterator;
035 import java.text.CharacterIterator;
036
037 /**
038 * Input method events contain information about text that is being
039 * composed using an input method. Whenever the text changes, the
040 * input method sends an event. If the text component that's currently
041 * using the input method is an active client, the event is dispatched
042 * to that component. Otherwise, it is dispatched to a separate
043 * composition window.
044 *
045 * <p>
046 * The text included with the input method event consists of two parts:
047 * committed text and composed text. Either part may be empty. The two
048 * parts together replace any uncommitted composed text sent in previous events,
049 * or the currently selected committed text.
050 * Committed text should be integrated into the text component's persistent
051 * data, it will not be sent again. Composed text may be sent repeatedly,
052 * with changes to reflect the user's editing operations. Committed text
053 * always precedes composed text.
054 *
055 * @author JavaSoft Asia/Pacific
056 * @version 1.32 06/05/07
057 * @since 1.2
058 */
059
060 public class InputMethodEvent extends AWTEvent {
061
062 /**
063 * Serial Version ID.
064 */
065 private static final long serialVersionUID = 4727190874778922661L;
066
067 /**
068 * Marks the first integer id for the range of input method event ids.
069 */
070 public static final int INPUT_METHOD_FIRST = 1100;
071
072 /**
073 * The event type indicating changed input method text. This event is
074 * generated by input methods while processing input.
075 */
076 public static final int INPUT_METHOD_TEXT_CHANGED = INPUT_METHOD_FIRST;
077
078 /**
079 * The event type indicating a changed insertion point in input method text.
080 * This event is
081 * generated by input methods while processing input if only the caret changed.
082 */
083 public static final int CARET_POSITION_CHANGED = INPUT_METHOD_FIRST + 1;
084
085 /**
086 * Marks the last integer id for the range of input method event ids.
087 */
088 public static final int INPUT_METHOD_LAST = INPUT_METHOD_FIRST + 1;
089
090 /**
091 * The time stamp that indicates when the event was created.
092 *
093 * @serial
094 * @see #getWhen
095 * @since 1.4
096 */
097 long when;
098
099 // Text object
100 private transient AttributedCharacterIterator text;
101 private transient int committedCharacterCount;
102 private transient TextHitInfo caret;
103 private transient TextHitInfo visiblePosition;
104
105 /**
106 * Constructs an <code>InputMethodEvent</code> with the specified
107 * source component, type, time, text, caret, and visiblePosition.
108 * <p>
109 * The offsets of caret and visiblePosition are relative to the current
110 * composed text; that is, the composed text within <code>text</code>
111 * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
112 * the composed text within the <code>text</code> of the
113 * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
114 * <p>Note that passing in an invalid <code>id</code> results in
115 * unspecified behavior. This method throws an
116 * <code>IllegalArgumentException</code> if <code>source</code>
117 * is <code>null</code>.
118 *
119 * @param source the object where the event originated
120 * @param id the event type
121 * @param when a long integer that specifies the time the event occurred
122 * @param text the combined committed and composed text,
123 * committed text first; must be <code>null</code>
124 * when the event type is <code>CARET_POSITION_CHANGED</code>;
125 * may be <code>null</code> for
126 * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no
127 * committed or composed text
128 * @param committedCharacterCount the number of committed
129 * characters in the text
130 * @param caret the caret (a.k.a. insertion point);
131 * <code>null</code> if there's no caret within current
132 * composed text
133 * @param visiblePosition the position that's most important
134 * to be visible; <code>null</code> if there's no
135 * recommendation for a visible position within current
136 * composed text
137 * @throws IllegalArgumentException if <code>id</code> is not
138 * in the range
139 * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>;
140 * or if id is <code>CARET_POSITION_CHANGED</code> and
141 * <code>text</code> is not <code>null</code>;
142 * or if <code>committedCharacterCount</code> is not in the range
143 * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code>
144 * @throws IllegalArgumentException if <code>source</code> is null
145 *
146 * @since 1.4
147 */
148 public InputMethodEvent(Component source, int id, long when,
149 AttributedCharacterIterator text,
150 int committedCharacterCount, TextHitInfo caret,
151 TextHitInfo visiblePosition) {
152 super (source, id);
153 if (id < INPUT_METHOD_FIRST || id > INPUT_METHOD_LAST) {
154 throw new IllegalArgumentException(
155 "id outside of valid range");
156 }
157
158 if (id == CARET_POSITION_CHANGED && text != null) {
159 throw new IllegalArgumentException(
160 "text must be null for CARET_POSITION_CHANGED");
161 }
162
163 this .when = when;
164 this .text = text;
165 int textLength = 0;
166 if (text != null) {
167 textLength = text.getEndIndex() - text.getBeginIndex();
168 }
169
170 if (committedCharacterCount < 0
171 || committedCharacterCount > textLength) {
172 throw new IllegalArgumentException(
173 "committedCharacterCount outside of valid range");
174 }
175 this .committedCharacterCount = committedCharacterCount;
176
177 this .caret = caret;
178 this .visiblePosition = visiblePosition;
179 }
180
181 /**
182 * Constructs an <code>InputMethodEvent</code> with the specified
183 * source component, type, text, caret, and visiblePosition.
184 * <p>
185 * The offsets of caret and visiblePosition are relative to the current
186 * composed text; that is, the composed text within <code>text</code>
187 * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
188 * the composed text within the <code>text</code> of the
189 * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
190 * The time stamp for this event is initialized by invoking
191 * {@link java.awt.EventQueue#getMostRecentEventTime()}.
192 * <p>Note that passing in an invalid <code>id</code> results in
193 * unspecified behavior. This method throws an
194 * <code>IllegalArgumentException</code> if <code>source</code>
195 * is <code>null</code>.
196 *
197 * @param source the object where the event originated
198 * @param id the event type
199 * @param text the combined committed and composed text,
200 * committed text first; must be <code>null</code>
201 * when the event type is <code>CARET_POSITION_CHANGED</code>;
202 * may be <code>null</code> for
203 * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no
204 * committed or composed text
205 * @param committedCharacterCount the number of committed
206 * characters in the text
207 * @param caret the caret (a.k.a. insertion point);
208 * <code>null</code> if there's no caret within current
209 * composed text
210 * @param visiblePosition the position that's most important
211 * to be visible; <code>null</code> if there's no
212 * recommendation for a visible position within current
213 * composed text
214 * @throws IllegalArgumentException if <code>id</code> is not
215 * in the range
216 * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>;
217 * or if id is <code>CARET_POSITION_CHANGED</code> and
218 * <code>text</code> is not <code>null</code>;
219 * or if <code>committedCharacterCount</code> is not in the range
220 * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code>
221 * @throws IllegalArgumentException if <code>source</code> is null
222 */
223 public InputMethodEvent(Component source, int id,
224 AttributedCharacterIterator text,
225 int committedCharacterCount, TextHitInfo caret,
226 TextHitInfo visiblePosition) {
227 this (source, id, EventQueue.getMostRecentEventTime(), text,
228 committedCharacterCount, caret, visiblePosition);
229 }
230
231 /**
232 * Constructs an <code>InputMethodEvent</code> with the
233 * specified source component, type, caret, and visiblePosition.
234 * The text is set to <code>null</code>,
235 * <code>committedCharacterCount</code> to 0.
236 * <p>
237 * The offsets of <code>caret</code> and <code>visiblePosition</code>
238 * are relative to the current composed text; that is,
239 * the composed text within the <code>text</code> of the
240 * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event if the
241 * event being constructed as a <code>CARET_POSITION_CHANGED</code> event.
242 * For an <code>INPUT_METHOD_TEXT_CHANGED</code> event without text,
243 * <code>caret</code> and <code>visiblePosition</code> must be
244 * <code>null</code>.
245 * The time stamp for this event is initialized by invoking
246 * {@link java.awt.EventQueue#getMostRecentEventTime()}.
247 * <p>Note that passing in an invalid <code>id</code> results in
248 * unspecified behavior. This method throws an
249 * <code>IllegalArgumentException</code> if <code>source</code>
250 * is <code>null</code>.
251 *
252 * @param source the object where the event originated
253 * @param id the event type
254 * @param caret the caret (a.k.a. insertion point);
255 * <code>null</code> if there's no caret within current
256 * composed text
257 * @param visiblePosition the position that's most important
258 * to be visible; <code>null</code> if there's no
259 * recommendation for a visible position within current
260 * composed text
261 * @throws IllegalArgumentException if <code>id</code> is not
262 * in the range
263 * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>
264 * @throws IllegalArgumentException if <code>source</code> is null
265 */
266 public InputMethodEvent(Component source, int id,
267 TextHitInfo caret, TextHitInfo visiblePosition) {
268 this (source, id, EventQueue.getMostRecentEventTime(), null, 0,
269 caret, visiblePosition);
270 }
271
272 /**
273 * Gets the combined committed and composed text.
274 * Characters from index 0 to index <code>getCommittedCharacterCount() - 1</code> are committed
275 * text, the remaining characters are composed text.
276 *
277 * @return the text.
278 * Always null for CARET_POSITION_CHANGED;
279 * may be null for INPUT_METHOD_TEXT_CHANGED if there's no composed or committed text.
280 */
281 public AttributedCharacterIterator getText() {
282 return text;
283 }
284
285 /**
286 * Gets the number of committed characters in the text.
287 */
288 public int getCommittedCharacterCount() {
289 return committedCharacterCount;
290 }
291
292 /**
293 * Gets the caret.
294 * <p>
295 * The offset of the caret is relative to the current
296 * composed text; that is, the composed text within getText()
297 * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
298 * the composed text within getText() of the
299 * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
300 *
301 * @return the caret (a.k.a. insertion point).
302 * Null if there's no caret within current composed text.
303 */
304 public TextHitInfo getCaret() {
305 return caret;
306 }
307
308 /**
309 * Gets the position that's most important to be visible.
310 * <p>
311 * The offset of the visible position is relative to the current
312 * composed text; that is, the composed text within getText()
313 * if this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event,
314 * the composed text within getText() of the
315 * preceding <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise.
316 *
317 * @return the position that's most important to be visible.
318 * Null if there's no recommendation for a visible position within current composed text.
319 */
320 public TextHitInfo getVisiblePosition() {
321 return visiblePosition;
322 }
323
324 /**
325 * Consumes this event so that it will not be processed
326 * in the default manner by the source which originated it.
327 */
328 public void consume() {
329 consumed = true;
330 }
331
332 /**
333 * Returns whether or not this event has been consumed.
334 * @see #consume
335 */
336 public boolean isConsumed() {
337 return consumed;
338 }
339
340 /**
341 * Returns the time stamp of when this event occurred.
342 *
343 * @return this event's timestamp
344 * @since 1.4
345 */
346 public long getWhen() {
347 return when;
348 }
349
350 /**
351 * Returns a parameter string identifying this event.
352 * This method is useful for event-logging and for debugging.
353 * It contains the event ID in text form, the characters of the
354 * committed and composed text
355 * separated by "+", the number of committed characters,
356 * the caret, and the visible position.
357 *
358 * @return a string identifying the event and its attributes
359 */
360 public String paramString() {
361 String typeStr;
362 switch (id) {
363 case INPUT_METHOD_TEXT_CHANGED:
364 typeStr = "INPUT_METHOD_TEXT_CHANGED";
365 break;
366 case CARET_POSITION_CHANGED:
367 typeStr = "CARET_POSITION_CHANGED";
368 break;
369 default:
370 typeStr = "unknown type";
371 }
372
373 String textString;
374 if (text == null) {
375 textString = "no text";
376 } else {
377 StringBuilder textBuffer = new StringBuilder("\"");
378 int committedCharacterCount = this .committedCharacterCount;
379 char c = text.first();
380 while (committedCharacterCount-- > 0) {
381 textBuffer.append(c);
382 c = text.next();
383 }
384 textBuffer.append("\" + \"");
385 while (c != CharacterIterator.DONE) {
386 textBuffer.append(c);
387 c = text.next();
388 }
389 textBuffer.append("\"");
390 textString = textBuffer.toString();
391 }
392
393 String countString = committedCharacterCount
394 + " characters committed";
395
396 String caretString;
397 if (caret == null) {
398 caretString = "no caret";
399 } else {
400 caretString = "caret: " + caret.toString();
401 }
402
403 String visiblePositionString;
404 if (visiblePosition == null) {
405 visiblePositionString = "no visible position";
406 } else {
407 visiblePositionString = "visible position: "
408 + visiblePosition.toString();
409 }
410
411 return typeStr + ", " + textString + ", " + countString + ", "
412 + caretString + ", " + visiblePositionString;
413 }
414
415 /**
416 * Initializes the <code>when</code> field if it is not present in the
417 * object input stream. In that case, the field will be initialized by
418 * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}.
419 */
420 private void readObject(ObjectInputStream s)
421 throws ClassNotFoundException, IOException {
422 s.defaultReadObject();
423 if (when == 0) {
424 when = EventQueue.getMostRecentEventTime();
425 }
426 }
427 }
|