author | ohair |
Tue, 28 Dec 2010 15:53:50 -0800 | |
changeset 7668 | d4a77089c587 |
parent 6839 | 1e57288ce186 |
child 14498 | 317e4103662e |
permissions | -rw-r--r-- |
2 | 1 |
/* |
7668 | 2 |
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 10 |
* |
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
5506 | 21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.awt; |
|
27 |
||
28 |
import java.util.Collections; |
|
29 |
import java.util.Locale; |
|
30 |
import java.util.Map; |
|
31 |
import java.util.HashMap; |
|
32 |
import java.awt.AWTEvent; |
|
33 |
import java.awt.AWTException; |
|
34 |
import java.awt.Component; |
|
35 |
import java.awt.Container; |
|
36 |
import java.awt.EventQueue; |
|
37 |
import java.awt.Window; |
|
38 |
import java.awt.im.InputContext; |
|
39 |
import java.awt.im.InputMethodHighlight; |
|
40 |
import java.awt.im.spi.InputMethodContext; |
|
41 |
import sun.awt.im.InputMethodAdapter; |
|
42 |
import java.awt.event.InputEvent; |
|
43 |
import java.awt.event.KeyEvent; |
|
44 |
import java.awt.event.MouseEvent; |
|
45 |
import java.awt.event.FocusEvent; |
|
46 |
import java.awt.event.ComponentEvent; |
|
47 |
import java.awt.event.WindowEvent; |
|
48 |
import java.awt.event.InputMethodEvent; |
|
49 |
import java.awt.font.TextAttribute; |
|
50 |
import java.awt.font.TextHitInfo; |
|
51 |
import java.awt.peer.ComponentPeer; |
|
52 |
import java.lang.Character.Subset; |
|
53 |
import java.text.AttributedString; |
|
54 |
import java.text.AttributedCharacterIterator; |
|
55 |
||
56 |
import java.io.File; |
|
57 |
import java.io.FileReader; |
|
58 |
import java.io.BufferedReader; |
|
59 |
import java.io.IOException; |
|
3938
ef327bd847c0
6879044: Eliminate the dependency on logging from the AWT/2D/Swing classes
mchung
parents:
2
diff
changeset
|
60 |
import sun.util.logging.PlatformLogger; |
2 | 61 |
import java.util.StringTokenizer; |
62 |
import java.util.regex.Pattern; |
|
63 |
||
64 |
||
65 |
/** |
|
66 |
* Input Method Adapter for XIM |
|
67 |
* |
|
68 |
* @author JavaSoft International |
|
69 |
*/ |
|
70 |
public abstract class X11InputMethod extends InputMethodAdapter { |
|
3938
ef327bd847c0
6879044: Eliminate the dependency on logging from the AWT/2D/Swing classes
mchung
parents:
2
diff
changeset
|
71 |
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11InputMethod"); |
2 | 72 |
/* |
73 |
* The following XIM* values must be the same as those defined in |
|
74 |
* Xlib.h |
|
75 |
*/ |
|
76 |
private static final int XIMReverse = (1<<0); |
|
77 |
private static final int XIMUnderline = (1<<1); |
|
78 |
private static final int XIMHighlight = (1<<2); |
|
79 |
private static final int XIMPrimary = (1<<5); |
|
80 |
private static final int XIMSecondary = (1<<6); |
|
81 |
private static final int XIMTertiary = (1<<7); |
|
82 |
||
83 |
/* |
|
84 |
* visible position values |
|
85 |
*/ |
|
86 |
private static final int XIMVisibleToForward = (1<<8); |
|
87 |
private static final int XIMVisibleToBackward = (1<<9); |
|
88 |
private static final int XIMVisibleCenter = (1<<10); |
|
89 |
private static final int XIMVisibleMask = (XIMVisibleToForward| |
|
90 |
XIMVisibleToBackward| |
|
91 |
XIMVisibleCenter); |
|
92 |
||
93 |
private Locale locale; |
|
94 |
private static boolean isXIMOpened = false; |
|
95 |
protected Container clientComponentWindow = null; |
|
96 |
private Component awtFocussedComponent = null; |
|
97 |
private Component lastXICFocussedComponent = null; |
|
98 |
private boolean isLastXICActive = false; |
|
6839
1e57288ce186
6575419: Solaris : XSetICFoucs is not called with Java application at appropriate timing
naoto
parents:
5506
diff
changeset
|
99 |
private boolean isLastTemporary = false; |
2 | 100 |
private boolean isActive = false; |
101 |
private boolean isActiveClient = false; |
|
102 |
private static Map[] highlightStyles; |
|
103 |
private boolean disposed = false; |
|
104 |
||
105 |
//reset the XIC if necessary |
|
106 |
private boolean needResetXIC = false; |
|
107 |
private Component needResetXICClient = null; |
|
108 |
||
109 |
// The use of compositionEnableSupported is to reduce unnecessary |
|
110 |
// native calls if set/isCompositionEnabled |
|
111 |
// throws UnsupportedOperationException. |
|
112 |
// It is set to false if that exception is thrown first time |
|
113 |
// either of the two methods are called. |
|
114 |
private boolean compositionEnableSupported = true; |
|
115 |
// The savedCompositionState indicates the composition mode when |
|
116 |
// endComposition or setCompositionEnabled is called. It doesn't always |
|
117 |
// reflect the actual composition state because it doesn't get updated |
|
118 |
// when the user changes the composition state through direct interaction |
|
119 |
// with the input method. It is used to save the composition mode when |
|
120 |
// focus is traversed across different client components sharing the |
|
121 |
// same java input context. Also if set/isCompositionEnabled are not |
|
122 |
// supported, it remains false. |
|
123 |
private boolean savedCompositionState = false; |
|
124 |
||
125 |
// variables to keep track of preedit context. |
|
126 |
// these variables need to be accessed within AWT_LOCK/UNLOCK |
|
127 |
private String committedText = null; |
|
128 |
private StringBuffer composedText = null; |
|
129 |
private IntBuffer rawFeedbacks; |
|
130 |
||
131 |
// private data (X11InputMethodData structure defined in |
|
132 |
// awt_InputMethod.c) for native methods |
|
133 |
// this structure needs to be accessed within AWT_LOCK/UNLOCK |
|
134 |
transient private long pData = 0; // accessed by native |
|
135 |
||
136 |
// Initialize highlight mapping table |
|
137 |
static { |
|
138 |
Map styles[] = new Map[4]; |
|
139 |
HashMap map; |
|
140 |
||
141 |
// UNSELECTED_RAW_TEXT_HIGHLIGHT |
|
142 |
map = new HashMap(1); |
|
143 |
map.put(TextAttribute.WEIGHT, |
|
144 |
TextAttribute.WEIGHT_BOLD); |
|
145 |
styles[0] = Collections.unmodifiableMap(map); |
|
146 |
||
147 |
// SELECTED_RAW_TEXT_HIGHLIGHT |
|
148 |
map = new HashMap(1); |
|
149 |
map.put(TextAttribute.SWAP_COLORS, |
|
150 |
TextAttribute.SWAP_COLORS_ON); |
|
151 |
styles[1] = Collections.unmodifiableMap(map); |
|
152 |
||
153 |
// UNSELECTED_CONVERTED_TEXT_HIGHLIGHT |
|
154 |
map = new HashMap(1); |
|
155 |
map.put(TextAttribute.INPUT_METHOD_UNDERLINE, |
|
156 |
TextAttribute.UNDERLINE_LOW_ONE_PIXEL); |
|
157 |
styles[2] = Collections.unmodifiableMap(map); |
|
158 |
||
159 |
// SELECTED_CONVERTED_TEXT_HIGHLIGHT |
|
160 |
map = new HashMap(1); |
|
161 |
map.put(TextAttribute.SWAP_COLORS, |
|
162 |
TextAttribute.SWAP_COLORS_ON); |
|
163 |
styles[3] = Collections.unmodifiableMap(map); |
|
164 |
||
165 |
highlightStyles = styles; |
|
166 |
} |
|
167 |
||
168 |
static { |
|
169 |
initIDs(); |
|
170 |
} |
|
171 |
||
172 |
/** |
|
173 |
* Initialize JNI field and method IDs for fields that may be |
|
174 |
accessed from C. |
|
175 |
*/ |
|
176 |
private static native void initIDs(); |
|
177 |
||
178 |
/** |
|
179 |
* Constructs an X11InputMethod instance. It initializes the XIM |
|
180 |
* environment if it's not done yet. |
|
181 |
* |
|
182 |
* @exception AWTException if XOpenIM() failed. |
|
183 |
*/ |
|
184 |
public X11InputMethod() throws AWTException { |
|
185 |
// supports only the locale in which the VM is started |
|
186 |
locale = X11InputMethodDescriptor.getSupportedLocale(); |
|
187 |
if (initXIM() == false) { |
|
188 |
throw new AWTException("Cannot open X Input Method"); |
|
189 |
} |
|
190 |
} |
|
191 |
||
192 |
protected void finalize() throws Throwable { |
|
193 |
dispose(); |
|
194 |
super.finalize(); |
|
195 |
} |
|
196 |
||
197 |
/** |
|
198 |
* Invokes openIM() that invokes XOpenIM() if it's not opened yet. |
|
199 |
* @return true if openXIM() is successful or it's already been opened. |
|
200 |
*/ |
|
201 |
private synchronized boolean initXIM() { |
|
202 |
if (isXIMOpened == false) |
|
203 |
isXIMOpened = openXIM(); |
|
204 |
return isXIMOpened; |
|
205 |
} |
|
206 |
||
207 |
protected abstract boolean openXIM(); |
|
208 |
||
209 |
protected boolean isDisposed() { |
|
210 |
return disposed; |
|
211 |
} |
|
212 |
||
213 |
protected abstract void setXICFocus(ComponentPeer peer, |
|
214 |
boolean value, boolean active); |
|
215 |
||
216 |
/** |
|
217 |
* Does nothing - this adapter doesn't use the input method context. |
|
218 |
* |
|
219 |
* @see java.awt.im.spi.InputMethod#setInputMethodContext |
|
220 |
*/ |
|
221 |
public void setInputMethodContext(InputMethodContext context) { |
|
222 |
} |
|
223 |
||
224 |
/** |
|
225 |
* Set locale to input. If input method doesn't support specified locale, |
|
226 |
* false will be returned and its behavior is not changed. |
|
227 |
* |
|
228 |
* @param lang locale to input |
|
229 |
* @return the true is returned when specified locale is supported. |
|
230 |
*/ |
|
231 |
public boolean setLocale(Locale lang) { |
|
232 |
if (lang.equals(locale)) { |
|
233 |
return true; |
|
234 |
} |
|
235 |
// special compatibility rule for Japanese and Korean |
|
236 |
if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) || |
|
237 |
locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) { |
|
238 |
return true; |
|
239 |
} |
|
240 |
return false; |
|
241 |
} |
|
242 |
||
243 |
/** |
|
244 |
* Returns current input locale. |
|
245 |
*/ |
|
246 |
public Locale getLocale() { |
|
247 |
return locale; |
|
248 |
} |
|
249 |
||
250 |
/** |
|
251 |
* Does nothing - XIM doesn't let you specify which characters you expect. |
|
252 |
* |
|
253 |
* @see java.awt.im.spi.InputMethod#setCharacterSubsets |
|
254 |
*/ |
|
255 |
public void setCharacterSubsets(Subset[] subsets) { |
|
256 |
} |
|
257 |
||
258 |
/** |
|
259 |
* Dispatch event to input method. InputContext dispatch event with this |
|
260 |
* method. Input method set consume flag if event is consumed in |
|
261 |
* input method. |
|
262 |
* |
|
263 |
* @param e event |
|
264 |
*/ |
|
265 |
public void dispatchEvent(AWTEvent e) { |
|
266 |
} |
|
267 |
||
268 |
||
269 |
protected final void resetXICifneeded(){ |
|
270 |
/* needResetXIC is used to indicate whether to call |
|
271 |
resetXIC on the active client. resetXIC will always be |
|
272 |
called on the passive client when endComposition is called. |
|
273 |
*/ |
|
274 |
if (needResetXIC && haveActiveClient() && |
|
275 |
getClientComponent() != needResetXICClient){ |
|
276 |
resetXIC(); |
|
277 |
||
278 |
// needs to reset the last xic focussed component. |
|
279 |
lastXICFocussedComponent = null; |
|
280 |
isLastXICActive = false; |
|
281 |
||
282 |
needResetXICClient = null; |
|
283 |
needResetXIC = false; |
|
284 |
} |
|
285 |
} |
|
286 |
||
287 |
/** |
|
288 |
* Reset the composition state to the current composition state. |
|
289 |
*/ |
|
290 |
private void resetCompositionState() { |
|
291 |
if (compositionEnableSupported) { |
|
292 |
try { |
|
293 |
/* Restore the composition mode to the last saved composition |
|
294 |
mode. */ |
|
295 |
setCompositionEnabled(savedCompositionState); |
|
296 |
} catch (UnsupportedOperationException e) { |
|
297 |
compositionEnableSupported = false; |
|
298 |
} |
|
299 |
} |
|
300 |
} |
|
301 |
||
302 |
/** |
|
303 |
* Query and then return the current composition state. |
|
304 |
* @returns the composition state if isCompositionEnabled call |
|
305 |
* is successful. Otherwise, it returns false. |
|
306 |
*/ |
|
307 |
private boolean getCompositionState() { |
|
308 |
boolean compositionState = false; |
|
309 |
if (compositionEnableSupported) { |
|
310 |
try { |
|
311 |
compositionState = isCompositionEnabled(); |
|
312 |
} catch (UnsupportedOperationException e) { |
|
313 |
compositionEnableSupported = false; |
|
314 |
} |
|
315 |
} |
|
316 |
return compositionState; |
|
317 |
} |
|
318 |
||
319 |
/** |
|
320 |
* Activate input method. |
|
321 |
*/ |
|
322 |
public synchronized void activate() { |
|
323 |
clientComponentWindow = getClientComponentWindow(); |
|
324 |
if (clientComponentWindow == null) |
|
325 |
return; |
|
326 |
||
327 |
if (lastXICFocussedComponent != null){ |
|
3938
ef327bd847c0
6879044: Eliminate the dependency on logging from the AWT/2D/Swing classes
mchung
parents:
2
diff
changeset
|
328 |
if (log.isLoggable(PlatformLogger.FINE)) log.fine("XICFocused {0}, AWTFocused {1}", |
ef327bd847c0
6879044: Eliminate the dependency on logging from the AWT/2D/Swing classes
mchung
parents:
2
diff
changeset
|
329 |
lastXICFocussedComponent, awtFocussedComponent); |
2 | 330 |
} |
331 |
||
332 |
if (pData == 0) { |
|
333 |
if (!createXIC()) { |
|
334 |
return; |
|
335 |
} |
|
336 |
disposed = false; |
|
337 |
} |
|
338 |
||
339 |
/* reset input context if necessary and set the XIC focus |
|
340 |
*/ |
|
341 |
resetXICifneeded(); |
|
342 |
ComponentPeer lastXICFocussedComponentPeer = null; |
|
343 |
ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent); |
|
344 |
||
345 |
if (lastXICFocussedComponent != null) { |
|
346 |
lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent); |
|
347 |
} |
|
348 |
||
349 |
/* If the last XIC focussed component has a different peer as the |
|
350 |
current focussed component, change the XIC focus to the newly |
|
351 |
focussed component. |
|
352 |
*/ |
|
6839
1e57288ce186
6575419: Solaris : XSetICFoucs is not called with Java application at appropriate timing
naoto
parents:
5506
diff
changeset
|
353 |
if (isLastTemporary || lastXICFocussedComponentPeer != awtFocussedComponentPeer || |
2 | 354 |
isLastXICActive != haveActiveClient()) { |
355 |
if (lastXICFocussedComponentPeer != null) { |
|
356 |
setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive); |
|
357 |
} |
|
358 |
if (awtFocussedComponentPeer != null) { |
|
359 |
setXICFocus(awtFocussedComponentPeer, true, haveActiveClient()); |
|
360 |
} |
|
361 |
lastXICFocussedComponent = awtFocussedComponent; |
|
362 |
isLastXICActive = haveActiveClient(); |
|
363 |
} |
|
364 |
resetCompositionState(); |
|
365 |
isActive = true; |
|
366 |
} |
|
367 |
||
368 |
protected abstract boolean createXIC(); |
|
369 |
||
370 |
/** |
|
371 |
* Deactivate input method. |
|
372 |
*/ |
|
373 |
public synchronized void deactivate(boolean isTemporary) { |
|
374 |
boolean isAc = haveActiveClient(); |
|
375 |
/* Usually as the client component, let's call it component A, |
|
376 |
loses the focus, this method is called. Then when another client |
|
377 |
component, let's call it component B, gets the focus, activate is first called on |
|
378 |
the previous focused compoent which is A, then endComposition is called on A, |
|
379 |
deactivate is called on A again. And finally activate is called on the newly |
|
380 |
focused component B. Here is the call sequence. |
|
381 |
||
382 |
A loses focus B gains focus |
|
383 |
-------------> deactivate A -------------> activate A -> endComposition A -> |
|
384 |
deactivate A -> activate B ----.... |
|
385 |
||
386 |
So in order to carry the composition mode across the components sharing the same |
|
387 |
input context, we save it when deactivate is called so that when activate is |
|
388 |
called, it can be restored correctly till activate is called on the newly focused |
|
389 |
component. (See also sun/awt/im/InputContext and bug 6184471). |
|
390 |
Last note, getCompositionState should be called before setXICFocus since |
|
391 |
setXICFocus here sets the XIC to 0. |
|
392 |
*/ |
|
393 |
savedCompositionState = getCompositionState(); |
|
394 |
||
395 |
if (isTemporary){ |
|
396 |
//turn the status window off... |
|
397 |
turnoffStatusWindow(); |
|
398 |
} |
|
399 |
||
400 |
/* Delay resetting the XIC focus until activate is called and the newly |
|
401 |
focussed component has a different peer as the last focussed component. |
|
402 |
*/ |
|
403 |
lastXICFocussedComponent = awtFocussedComponent; |
|
404 |
isLastXICActive = isAc; |
|
6839
1e57288ce186
6575419: Solaris : XSetICFoucs is not called with Java application at appropriate timing
naoto
parents:
5506
diff
changeset
|
405 |
isLastTemporary = isTemporary; |
2 | 406 |
isActive = false; |
407 |
} |
|
408 |
||
409 |
/** |
|
410 |
* Explicitly disable the native IME. Native IME is not disabled when |
|
411 |
* deactivate is called. |
|
412 |
*/ |
|
413 |
public void disableInputMethod() { |
|
414 |
if (lastXICFocussedComponent != null) { |
|
415 |
setXICFocus(getPeer(lastXICFocussedComponent), false, isLastXICActive); |
|
416 |
lastXICFocussedComponent = null; |
|
417 |
isLastXICActive = false; |
|
418 |
} |
|
419 |
} |
|
420 |
||
421 |
// implements java.awt.im.spi.InputMethod.hideWindows |
|
422 |
public void hideWindows() { |
|
423 |
// ??? need real implementation |
|
424 |
} |
|
425 |
||
426 |
/** |
|
427 |
* @see java.awt.Toolkit#mapInputMethodHighlight |
|
428 |
*/ |
|
429 |
public static Map mapInputMethodHighlight(InputMethodHighlight highlight) { |
|
430 |
int index; |
|
431 |
int state = highlight.getState(); |
|
432 |
if (state == InputMethodHighlight.RAW_TEXT) { |
|
433 |
index = 0; |
|
434 |
} else if (state == InputMethodHighlight.CONVERTED_TEXT) { |
|
435 |
index = 2; |
|
436 |
} else { |
|
437 |
return null; |
|
438 |
} |
|
439 |
if (highlight.isSelected()) { |
|
440 |
index += 1; |
|
441 |
} |
|
442 |
return highlightStyles[index]; |
|
443 |
} |
|
444 |
||
445 |
/** |
|
446 |
* @see sun.awt.im.InputMethodAdapter#setAWTFocussedComponent |
|
447 |
*/ |
|
448 |
protected void setAWTFocussedComponent(Component component) { |
|
449 |
if (component == null) { |
|
450 |
return; |
|
451 |
} |
|
452 |
if (isActive) { |
|
453 |
// deactivate/activate are being suppressed during a focus change - |
|
454 |
// this may happen when an input method window is made visible |
|
455 |
boolean ac = haveActiveClient(); |
|
456 |
setXICFocus(getPeer(awtFocussedComponent), false, ac); |
|
457 |
setXICFocus(getPeer(component), true, ac); |
|
458 |
} |
|
459 |
awtFocussedComponent = component; |
|
460 |
} |
|
461 |
||
462 |
/** |
|
463 |
* @see sun.awt.im.InputMethodAdapter#stopListening |
|
464 |
*/ |
|
465 |
protected void stopListening() { |
|
466 |
// It is desirable to disable XIM by calling XSetICValues with |
|
467 |
// XNPreeditState == XIMPreeditDisable. But Solaris 2.6 and |
|
468 |
// Solaris 7 do not implement this correctly without a patch, |
|
469 |
// so just call resetXIC here. Prior endComposition call commits |
|
470 |
// the existing composed text. |
|
471 |
endComposition(); |
|
472 |
// disable the native input method so that the other input |
|
473 |
// method could get the input focus. |
|
474 |
disableInputMethod(); |
|
475 |
if (needResetXIC) { |
|
476 |
resetXIC(); |
|
477 |
needResetXICClient = null; |
|
478 |
needResetXIC = false; |
|
479 |
} |
|
480 |
} |
|
481 |
||
482 |
/** |
|
483 |
* Returns the Window instance in which the client component is |
|
484 |
* contained. If not found, null is returned. (IS THIS POSSIBLE?) |
|
485 |
*/ |
|
486 |
// NOTE: This method may be called by privileged threads. |
|
487 |
// DO NOT INVOKE CLIENT CODE ON THIS THREAD! |
|
488 |
private Window getClientComponentWindow() { |
|
489 |
Component client = getClientComponent(); |
|
490 |
Container container; |
|
491 |
||
492 |
if (client instanceof Container) { |
|
493 |
container = (Container) client; |
|
494 |
} else { |
|
495 |
container = getParent(client); |
|
496 |
} |
|
497 |
||
498 |
while (container != null && !(container instanceof java.awt.Window)) { |
|
499 |
container = getParent(container); |
|
500 |
} |
|
501 |
return (Window) container; |
|
502 |
} |
|
503 |
||
504 |
protected abstract Container getParent(Component client); |
|
505 |
||
506 |
/** |
|
507 |
* Returns peer of the given client component. If the given client component |
|
508 |
* doesn't have peer, peer of the native container of the client is returned. |
|
509 |
*/ |
|
510 |
protected abstract ComponentPeer getPeer(Component client); |
|
511 |
||
512 |
/** |
|
513 |
* Used to protect preedit data |
|
514 |
*/ |
|
515 |
protected abstract void awtLock(); |
|
516 |
protected abstract void awtUnlock(); |
|
517 |
||
518 |
/** |
|
519 |
* Creates an input method event from the arguments given |
|
520 |
* and posts it on the AWT event queue. For arguments, |
|
521 |
* see InputMethodEvent. Called by input method. |
|
522 |
* |
|
523 |
* @see java.awt.event.InputMethodEvent#InputMethodEvent |
|
524 |
*/ |
|
525 |
private void postInputMethodEvent(int id, |
|
526 |
AttributedCharacterIterator text, |
|
527 |
int committedCharacterCount, |
|
528 |
TextHitInfo caret, |
|
529 |
TextHitInfo visiblePosition, |
|
530 |
long when) { |
|
531 |
Component source = getClientComponent(); |
|
532 |
if (source != null) { |
|
533 |
InputMethodEvent event = new InputMethodEvent(source, |
|
534 |
id, when, text, committedCharacterCount, caret, visiblePosition); |
|
535 |
SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event); |
|
536 |
} |
|
537 |
} |
|
538 |
||
539 |
private void postInputMethodEvent(int id, |
|
540 |
AttributedCharacterIterator text, |
|
541 |
int committedCharacterCount, |
|
542 |
TextHitInfo caret, |
|
543 |
TextHitInfo visiblePosition) { |
|
544 |
postInputMethodEvent(id, text, committedCharacterCount, |
|
545 |
caret, visiblePosition, EventQueue.getMostRecentEventTime()); |
|
546 |
} |
|
547 |
||
548 |
/** |
|
549 |
* Dispatches committed text from XIM to the awt event queue. This |
|
550 |
* method is invoked from the event handler in canvas.c in the |
|
551 |
* AWT Toolkit thread context and thus inside the AWT Lock. |
|
552 |
* @param str committed text |
|
553 |
* @param long when |
|
554 |
*/ |
|
555 |
// NOTE: This method may be called by privileged threads. |
|
556 |
// This functionality is implemented in a package-private method |
|
557 |
// to insure that it cannot be overridden by client subclasses. |
|
558 |
// DO NOT INVOKE CLIENT CODE ON THIS THREAD! |
|
559 |
void dispatchCommittedText(String str, long when) { |
|
560 |
if (str == null) |
|
561 |
return; |
|
562 |
||
563 |
if (composedText == null) { |
|
564 |
AttributedString attrstr = new AttributedString(str); |
|
565 |
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, |
|
566 |
attrstr.getIterator(), |
|
567 |
str.length(), |
|
568 |
null, |
|
569 |
null, |
|
570 |
when); |
|
571 |
} else { |
|
572 |
// if there is composed text, wait until the preedit |
|
573 |
// callback is invoked. |
|
574 |
committedText = str; |
|
575 |
} |
|
576 |
} |
|
577 |
||
578 |
private void dispatchCommittedText(String str) { |
|
579 |
dispatchCommittedText(str, EventQueue.getMostRecentEventTime()); |
|
580 |
} |
|
581 |
||
582 |
/** |
|
583 |
* Updates composed text with XIM preedit information and |
|
584 |
* posts composed text to the awt event queue. The args of |
|
585 |
* this method correspond to the XIM preedit callback |
|
586 |
* information. The XIM highlight attributes are translated via |
|
587 |
* fixed mapping (i.e., independent from any underlying input |
|
588 |
* method engine). This method is invoked in the AWT Toolkit |
|
589 |
* (X event loop) thread context and thus inside the AWT Lock. |
|
590 |
*/ |
|
591 |
// NOTE: This method may be called by privileged threads. |
|
592 |
// This functionality is implemented in a package-private method |
|
593 |
// to insure that it cannot be overridden by client subclasses. |
|
594 |
// DO NOT INVOKE CLIENT CODE ON THIS THREAD! |
|
595 |
void dispatchComposedText(String chgText, |
|
596 |
int chgStyles[], |
|
597 |
int chgOffset, |
|
598 |
int chgLength, |
|
599 |
int caretPosition, |
|
600 |
long when) { |
|
601 |
if (disposed) { |
|
602 |
return; |
|
603 |
} |
|
604 |
||
605 |
//Workaround for deadlock bug on solaris2.6_zh bug#4170760 |
|
606 |
if (chgText == null |
|
607 |
&& chgStyles == null |
|
608 |
&& chgOffset == 0 |
|
609 |
&& chgLength == 0 |
|
610 |
&& caretPosition == 0 |
|
611 |
&& composedText == null |
|
612 |
&& committedText == null) |
|
613 |
return; |
|
614 |
||
615 |
if (composedText == null) { |
|
616 |
// TODO: avoid reallocation of those buffers |
|
617 |
composedText = new StringBuffer(INITIAL_SIZE); |
|
618 |
rawFeedbacks = new IntBuffer(INITIAL_SIZE); |
|
619 |
} |
|
620 |
if (chgLength > 0) { |
|
621 |
if (chgText == null && chgStyles != null) { |
|
622 |
rawFeedbacks.replace(chgOffset, chgStyles); |
|
623 |
} else { |
|
624 |
if (chgLength == composedText.length()) { |
|
625 |
// optimization for the special case to replace the |
|
626 |
// entire previous text |
|
627 |
composedText = new StringBuffer(INITIAL_SIZE); |
|
628 |
rawFeedbacks = new IntBuffer(INITIAL_SIZE); |
|
629 |
} else { |
|
630 |
if (composedText.length() > 0) { |
|
631 |
if (chgOffset+chgLength < composedText.length()) { |
|
632 |
String text; |
|
633 |
text = composedText.toString().substring(chgOffset+chgLength, |
|
634 |
composedText.length()); |
|
635 |
composedText.setLength(chgOffset); |
|
636 |
composedText.append(text); |
|
637 |
} else { |
|
638 |
// in case to remove substring from chgOffset |
|
639 |
// to the end |
|
640 |
composedText.setLength(chgOffset); |
|
641 |
} |
|
642 |
rawFeedbacks.remove(chgOffset, chgLength); |
|
643 |
} |
|
644 |
} |
|
645 |
} |
|
646 |
} |
|
647 |
if (chgText != null) { |
|
648 |
composedText.insert(chgOffset, chgText); |
|
649 |
if (chgStyles != null) |
|
650 |
rawFeedbacks.insert(chgOffset, chgStyles); |
|
651 |
} |
|
652 |
||
653 |
if (composedText.length() == 0) { |
|
654 |
composedText = null; |
|
655 |
rawFeedbacks = null; |
|
656 |
||
657 |
// if there is any outstanding committed text stored by |
|
658 |
// dispatchCommittedText(), it has to be sent to the |
|
659 |
// client component. |
|
660 |
if (committedText != null) { |
|
661 |
dispatchCommittedText(committedText, when); |
|
662 |
committedText = null; |
|
663 |
return; |
|
664 |
} |
|
665 |
||
666 |
// otherwise, send null text to delete client's composed |
|
667 |
// text. |
|
668 |
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, |
|
669 |
null, |
|
670 |
0, |
|
671 |
null, |
|
672 |
null, |
|
673 |
when); |
|
674 |
||
675 |
return; |
|
676 |
} |
|
677 |
||
678 |
// Now sending the composed text to the client |
|
679 |
int composedOffset; |
|
680 |
AttributedString inputText; |
|
681 |
||
682 |
// if there is any partially committed text, concatenate it to |
|
683 |
// the composed text. |
|
684 |
if (committedText != null) { |
|
685 |
composedOffset = committedText.length(); |
|
686 |
inputText = new AttributedString(committedText + composedText); |
|
687 |
committedText = null; |
|
688 |
} else { |
|
689 |
composedOffset = 0; |
|
690 |
inputText = new AttributedString(composedText.toString()); |
|
691 |
} |
|
692 |
||
693 |
int currentFeedback; |
|
694 |
int nextFeedback; |
|
695 |
int startOffset = 0; |
|
696 |
int currentOffset; |
|
697 |
int visiblePosition = 0; |
|
698 |
TextHitInfo visiblePositionInfo = null; |
|
699 |
||
700 |
rawFeedbacks.rewind(); |
|
701 |
currentFeedback = rawFeedbacks.getNext(); |
|
702 |
rawFeedbacks.unget(); |
|
703 |
while ((nextFeedback = rawFeedbacks.getNext()) != -1) { |
|
704 |
if (visiblePosition == 0) { |
|
705 |
visiblePosition = nextFeedback & XIMVisibleMask; |
|
706 |
if (visiblePosition != 0) { |
|
707 |
int index = rawFeedbacks.getOffset() - 1; |
|
708 |
||
709 |
if (visiblePosition == XIMVisibleToBackward) |
|
710 |
visiblePositionInfo = TextHitInfo.leading(index); |
|
711 |
else |
|
712 |
visiblePositionInfo = TextHitInfo.trailing(index); |
|
713 |
} |
|
714 |
} |
|
715 |
nextFeedback &= ~XIMVisibleMask; |
|
716 |
if (currentFeedback != nextFeedback) { |
|
717 |
rawFeedbacks.unget(); |
|
718 |
currentOffset = rawFeedbacks.getOffset(); |
|
719 |
inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, |
|
720 |
convertVisualFeedbackToHighlight(currentFeedback), |
|
721 |
composedOffset + startOffset, |
|
722 |
composedOffset + currentOffset); |
|
723 |
startOffset = currentOffset; |
|
724 |
currentFeedback = nextFeedback; |
|
725 |
} |
|
726 |
} |
|
727 |
currentOffset = rawFeedbacks.getOffset(); |
|
728 |
if (currentOffset >= 0) { |
|
729 |
inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, |
|
730 |
convertVisualFeedbackToHighlight(currentFeedback), |
|
731 |
composedOffset + startOffset, |
|
732 |
composedOffset + currentOffset); |
|
733 |
} |
|
734 |
||
735 |
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, |
|
736 |
inputText.getIterator(), |
|
737 |
composedOffset, |
|
738 |
TextHitInfo.leading(caretPosition), |
|
739 |
visiblePositionInfo, |
|
740 |
when); |
|
741 |
} |
|
742 |
||
743 |
/** |
|
744 |
* Flushes composed and committed text held in this context. |
|
745 |
* This method is invoked in the AWT Toolkit (X event loop) thread context |
|
746 |
* and thus inside the AWT Lock. |
|
747 |
*/ |
|
748 |
// NOTE: This method may be called by privileged threads. |
|
749 |
// This functionality is implemented in a package-private method |
|
750 |
// to insure that it cannot be overridden by client subclasses. |
|
751 |
// DO NOT INVOKE CLIENT CODE ON THIS THREAD! |
|
752 |
void flushText() { |
|
753 |
String flush = (committedText != null ? committedText : ""); |
|
754 |
if (composedText != null) { |
|
755 |
flush += composedText.toString(); |
|
756 |
} |
|
757 |
||
758 |
if (!flush.equals("")) { |
|
759 |
AttributedString attrstr = new AttributedString(flush); |
|
760 |
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, |
|
761 |
attrstr.getIterator(), |
|
762 |
flush.length(), |
|
763 |
null, |
|
764 |
null, |
|
765 |
EventQueue.getMostRecentEventTime()); |
|
766 |
composedText = null; |
|
767 |
committedText = null; |
|
768 |
} |
|
769 |
} |
|
770 |
||
771 |
/* |
|
772 |
* Subclasses should override disposeImpl() instead of dispose(). Client |
|
773 |
* code should always invoke dispose(), never disposeImpl(). |
|
774 |
*/ |
|
775 |
protected synchronized void disposeImpl() { |
|
776 |
disposeXIC(); |
|
777 |
awtLock(); |
|
778 |
composedText = null; |
|
779 |
committedText = null; |
|
780 |
rawFeedbacks = null; |
|
781 |
awtUnlock(); |
|
782 |
awtFocussedComponent = null; |
|
783 |
lastXICFocussedComponent = null; |
|
784 |
} |
|
785 |
||
786 |
/** |
|
787 |
* Frees all X Window resources associated with this object. |
|
788 |
* |
|
789 |
* @see java.awt.im.spi.InputMethod#dispose |
|
790 |
*/ |
|
791 |
public final void dispose() { |
|
792 |
boolean call_disposeImpl = false; |
|
793 |
||
794 |
if (!disposed) { |
|
795 |
synchronized (this) { |
|
796 |
if (!disposed) { |
|
797 |
disposed = call_disposeImpl = true; |
|
798 |
} |
|
799 |
} |
|
800 |
} |
|
801 |
||
802 |
if (call_disposeImpl) { |
|
803 |
disposeImpl(); |
|
804 |
} |
|
805 |
} |
|
806 |
||
807 |
/** |
|
808 |
* Returns null. |
|
809 |
* |
|
810 |
* @see java.awt.im.spi.InputMethod#getControlObject |
|
811 |
*/ |
|
812 |
public Object getControlObject() { |
|
813 |
return null; |
|
814 |
} |
|
815 |
||
816 |
/** |
|
817 |
* @see java.awt.im.spi.InputMethod#removeNotify |
|
818 |
*/ |
|
819 |
public synchronized void removeNotify() { |
|
820 |
dispose(); |
|
821 |
} |
|
822 |
||
823 |
/** |
|
824 |
* @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean) |
|
825 |
*/ |
|
826 |
public void setCompositionEnabled(boolean enable) { |
|
827 |
/* If the composition state is successfully changed, set |
|
828 |
the savedCompositionState to 'enable'. Otherwise, simply |
|
829 |
return. |
|
830 |
setCompositionEnabledNative may throw UnsupportedOperationException. |
|
831 |
Don't try to catch it since the method may be called by clients. |
|
832 |
Use package private mthod 'resetCompositionState' if you want the |
|
833 |
exception to be caught. |
|
834 |
*/ |
|
835 |
if (setCompositionEnabledNative(enable)) { |
|
836 |
savedCompositionState = enable; |
|
837 |
} |
|
838 |
} |
|
839 |
||
840 |
/** |
|
841 |
* @see java.awt.im.spi.InputMethod#isCompositionEnabled |
|
842 |
*/ |
|
843 |
public boolean isCompositionEnabled() { |
|
844 |
/* isCompositionEnabledNative may throw UnsupportedOperationException. |
|
845 |
Don't try to catch it since this method may be called by clients. |
|
846 |
Use package private method 'getCompositionState' if you want the |
|
847 |
exception to be caught. |
|
848 |
*/ |
|
849 |
return isCompositionEnabledNative(); |
|
850 |
} |
|
851 |
||
852 |
/** |
|
853 |
* Ends any input composition that may currently be going on in this |
|
854 |
* context. Depending on the platform and possibly user preferences, |
|
855 |
* this may commit or delete uncommitted text. Any changes to the text |
|
856 |
* are communicated to the active component using an input method event. |
|
857 |
* |
|
858 |
* <p> |
|
859 |
* A text editing component may call this in a variety of situations, |
|
860 |
* for example, when the user moves the insertion point within the text |
|
861 |
* (but outside the composed text), or when the component's text is |
|
862 |
* saved to a file or copied to the clipboard. |
|
863 |
* |
|
864 |
*/ |
|
865 |
public void endComposition() { |
|
866 |
if (disposed) { |
|
867 |
return; |
|
868 |
} |
|
869 |
||
870 |
/* Before calling resetXIC, record the current composition mode |
|
871 |
so that it can be restored later. */ |
|
872 |
savedCompositionState = getCompositionState(); |
|
873 |
boolean active = haveActiveClient(); |
|
874 |
if (active && composedText == null && committedText == null){ |
|
875 |
needResetXIC = true; |
|
876 |
needResetXICClient = getClientComponent(); |
|
877 |
return; |
|
878 |
} |
|
879 |
||
880 |
String text = resetXIC(); |
|
881 |
/* needResetXIC is only set to true for active client. So passive |
|
882 |
client should not reset the flag to false. */ |
|
883 |
if (active) { |
|
884 |
needResetXIC = false; |
|
885 |
} |
|
886 |
||
887 |
// Remove any existing composed text by posting an InputMethodEvent |
|
888 |
// with null composed text. It would be desirable to wait for a |
|
889 |
// dispatchComposedText call from X input method engine, but some |
|
890 |
// input method does not conform to the XIM specification and does |
|
891 |
// not call the preedit callback to erase preedit text on calling |
|
892 |
// XmbResetIC. To work around this problem, do it here by ourselves. |
|
893 |
awtLock(); |
|
894 |
composedText = null; |
|
895 |
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, |
|
896 |
null, |
|
897 |
0, |
|
898 |
null, |
|
899 |
null); |
|
900 |
||
901 |
if (text != null && text.length() > 0) { |
|
902 |
dispatchCommittedText(text); |
|
903 |
} |
|
904 |
awtUnlock(); |
|
905 |
||
906 |
// Restore the preedit state if it was enabled |
|
907 |
if (savedCompositionState) { |
|
908 |
resetCompositionState(); |
|
909 |
} |
|
910 |
} |
|
911 |
||
912 |
/** |
|
913 |
* Returns a string with information about the current input method server, or null. |
|
914 |
* On both Linux & SunOS, the value of environment variable XMODIFIERS is |
|
915 |
* returned if set. Otherwise, on SunOS, $HOME/.dtprofile will be parsed |
|
916 |
* to find out the language service engine (atok or wnn) since there is |
|
917 |
* no API in Xlib which returns the information of native |
|
918 |
* IM server or language service and we want to try our best to return as much |
|
919 |
* information as possible. |
|
920 |
* |
|
921 |
* Note: This method could return null on Linux if XMODIFIERS is not set properly or |
|
922 |
* if any IOException is thrown. |
|
923 |
* See man page of XSetLocaleModifiers(3X11) for the usgae of XMODIFIERS, |
|
924 |
* atok12setup(1) and wnn6setup(1) for the information written to |
|
925 |
* $HOME/.dtprofile when you run these two commands. |
|
926 |
* |
|
927 |
*/ |
|
928 |
public String getNativeInputMethodInfo() { |
|
929 |
String xmodifiers = System.getenv("XMODIFIERS"); |
|
930 |
String imInfo = null; |
|
931 |
||
932 |
// If XMODIFIERS is set, return the value |
|
933 |
if (xmodifiers != null) { |
|
934 |
int imIndex = xmodifiers.indexOf("@im="); |
|
935 |
if (imIndex != -1) { |
|
936 |
imInfo = xmodifiers.substring(imIndex + 4); |
|
937 |
} |
|
938 |
} else if (System.getProperty("os.name").startsWith("SunOS")) { |
|
939 |
File dtprofile = new File(System.getProperty("user.home") + |
|
940 |
"/.dtprofile"); |
|
941 |
String languageEngineInfo = null; |
|
942 |
try { |
|
943 |
BufferedReader br = new BufferedReader(new FileReader(dtprofile)); |
|
944 |
String line = null; |
|
945 |
||
946 |
while ( languageEngineInfo == null && (line = br.readLine()) != null) { |
|
947 |
if (line.contains("atok") || line.contains("wnn")) { |
|
948 |
StringTokenizer tokens = new StringTokenizer(line); |
|
949 |
while (tokens.hasMoreTokens()) { |
|
950 |
String token = tokens.nextToken(); |
|
951 |
if (Pattern.matches("atok.*setup", token) || |
|
952 |
Pattern.matches("wnn.*setup", token)){ |
|
953 |
languageEngineInfo = token.substring(0, token.indexOf("setup")); |
|
954 |
break; |
|
955 |
} |
|
956 |
} |
|
957 |
} |
|
958 |
} |
|
959 |
||
960 |
br.close(); |
|
961 |
} catch(IOException ioex) { |
|
962 |
// Since this method is provided for internal testing only, |
|
963 |
// we dump the stack trace for the ease of debugging. |
|
964 |
ioex.printStackTrace(); |
|
965 |
} |
|
966 |
||
967 |
imInfo = "htt " + languageEngineInfo; |
|
968 |
} |
|
969 |
||
970 |
return imInfo; |
|
971 |
} |
|
972 |
||
973 |
||
974 |
/** |
|
975 |
* Performs mapping from an XIM visible feedback value to Java IM highlight. |
|
976 |
* @return Java input method highlight |
|
977 |
*/ |
|
978 |
private InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) { |
|
979 |
InputMethodHighlight highlight; |
|
980 |
||
981 |
switch (feedback) { |
|
982 |
case XIMUnderline: |
|
983 |
highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT; |
|
984 |
break; |
|
985 |
case XIMReverse: |
|
986 |
highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT; |
|
987 |
break; |
|
988 |
case XIMHighlight: |
|
989 |
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; |
|
990 |
break; |
|
991 |
case XIMPrimary: |
|
992 |
highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT; |
|
993 |
break; |
|
994 |
case XIMSecondary: |
|
995 |
highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT; |
|
996 |
break; |
|
997 |
case XIMTertiary: |
|
998 |
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; |
|
999 |
break; |
|
1000 |
default: |
|
1001 |
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; |
|
1002 |
break; |
|
1003 |
} |
|
1004 |
return highlight; |
|
1005 |
} |
|
1006 |
||
1007 |
// initial capacity size for string buffer, etc. |
|
1008 |
private static final int INITIAL_SIZE = 64; |
|
1009 |
||
1010 |
/** |
|
1011 |
* IntBuffer is an inner class that manipulates an int array and |
|
1012 |
* provides UNIX file io stream-like programming interfaces to |
|
1013 |
* access it. (An alternative would be to use ArrayList which may |
|
1014 |
* be too expensive for the work.) |
|
1015 |
*/ |
|
1016 |
private final class IntBuffer { |
|
1017 |
private int[] intArray; |
|
1018 |
private int size; |
|
1019 |
private int index; |
|
1020 |
||
1021 |
IntBuffer(int initialCapacity) { |
|
1022 |
intArray = new int[initialCapacity]; |
|
1023 |
size = 0; |
|
1024 |
index = 0; |
|
1025 |
} |
|
1026 |
||
1027 |
void insert(int offset, int[] values) { |
|
1028 |
int newSize = size + values.length; |
|
1029 |
if (intArray.length < newSize) { |
|
1030 |
int[] newIntArray = new int[newSize * 2]; |
|
1031 |
System.arraycopy(intArray, 0, newIntArray, 0, size); |
|
1032 |
intArray = newIntArray; |
|
1033 |
} |
|
1034 |
System.arraycopy(intArray, offset, intArray, offset+values.length, |
|
1035 |
size - offset); |
|
1036 |
System.arraycopy(values, 0, intArray, offset, values.length); |
|
1037 |
size += values.length; |
|
1038 |
if (index > offset) |
|
1039 |
index = offset; |
|
1040 |
} |
|
1041 |
||
1042 |
void remove(int offset, int length) { |
|
1043 |
if (offset + length != size) |
|
1044 |
System.arraycopy(intArray, offset+length, intArray, offset, |
|
1045 |
size - offset - length); |
|
1046 |
size -= length; |
|
1047 |
if (index > offset) |
|
1048 |
index = offset; |
|
1049 |
} |
|
1050 |
||
1051 |
void replace(int offset, int[] values) { |
|
1052 |
System.arraycopy(values, 0, intArray, offset, values.length); |
|
1053 |
} |
|
1054 |
||
1055 |
void removeAll() { |
|
1056 |
size = 0; |
|
1057 |
index = 0; |
|
1058 |
} |
|
1059 |
||
1060 |
void rewind() { |
|
1061 |
index = 0; |
|
1062 |
} |
|
1063 |
||
1064 |
int getNext() { |
|
1065 |
if (index == size) |
|
1066 |
return -1; |
|
1067 |
return intArray[index++]; |
|
1068 |
} |
|
1069 |
||
1070 |
void unget() { |
|
1071 |
if (index != 0) |
|
1072 |
index--; |
|
1073 |
} |
|
1074 |
||
1075 |
int getOffset() { |
|
1076 |
return index; |
|
1077 |
} |
|
1078 |
||
1079 |
public String toString() { |
|
1080 |
StringBuffer s = new StringBuffer(); |
|
1081 |
for (int i = 0; i < size;) { |
|
1082 |
s.append(intArray[i++]); |
|
1083 |
if (i < size) |
|
1084 |
s.append(","); |
|
1085 |
} |
|
1086 |
return s.toString(); |
|
1087 |
} |
|
1088 |
} |
|
1089 |
||
1090 |
/* |
|
1091 |
* Native methods |
|
1092 |
*/ |
|
1093 |
protected native String resetXIC(); |
|
1094 |
private native void disposeXIC(); |
|
1095 |
private native boolean setCompositionEnabledNative(boolean enable); |
|
1096 |
private native boolean isCompositionEnabledNative(); |
|
1097 |
private native void turnoffStatusWindow(); |
|
1098 |
} |