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