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();
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
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());
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;
697 TextHitInfo visiblePositionInfo = null;
698
699 rawFeedbacks.rewind();
700 currentFeedback = rawFeedbacks.getNext();
701 rawFeedbacks.unget();
702 while ((nextFeedback = rawFeedbacks.getNext()) != -1) {
703 if (visiblePosition == 0) {
704 visiblePosition = nextFeedback & XIMVisibleMask;
705 if (visiblePosition != 0) {
706 int index = rawFeedbacks.getOffset() - 1;
707
708 if (visiblePosition == XIMVisibleToBackward)
709 visiblePositionInfo = TextHitInfo.leading(index);
710 else
711 visiblePositionInfo = TextHitInfo.trailing(index);
712 }
713 }
714 nextFeedback &= ~XIMVisibleMask;
715 if (currentFeedback != nextFeedback) {
716 rawFeedbacks.unget();
717 currentOffset = rawFeedbacks.getOffset();
718 inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
719 convertVisualFeedbackToHighlight(currentFeedback),
720 composedOffset + startOffset,
721 composedOffset + currentOffset);
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 }
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>
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
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);
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.AWTEvent;
29 import java.awt.AWTException;
30 import java.awt.Component;
31 import java.awt.Container;
32 import java.awt.EventQueue;
33 import java.awt.Window;
34 import java.awt.event.InputMethodEvent;
35 import java.awt.font.TextAttribute;
36 import java.awt.font.TextHitInfo;
37 import java.awt.im.InputMethodHighlight;
38 import java.awt.im.spi.InputMethodContext;
39 import java.awt.peer.ComponentPeer;
40 import java.io.BufferedReader;
41 import java.io.File;
42 import java.io.FileReader;
43 import java.io.IOException;
44 import java.lang.Character.Subset;
45 import java.lang.ref.WeakReference;
46 import java.text.AttributedCharacterIterator;
47 import java.text.AttributedString;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.Locale;
51 import java.util.Map;
52 import java.util.StringTokenizer;
53 import java.util.regex.Pattern;
54
55 import sun.awt.im.InputMethodAdapter;
56 import sun.util.logging.PlatformLogger;
57
58 /**
59 * Input Method Adapter for XIM
60 *
61 * @author JavaSoft International
62 */
63 public abstract class X11InputMethodBase extends InputMethodAdapter {
64 protected static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11InputMethod");
65 /*
66 * The following XIM* values must be the same as those defined in
67 * Xlib.h
68 */
69 private static final int XIMReverse = (1<<0);
70 private static final int XIMUnderline = (1<<1);
71 private static final int XIMHighlight = (1<<2);
72 private static final int XIMPrimary = (1<<5);
73 private static final int XIMSecondary = (1<<6);
74 private static final int XIMTertiary = (1<<7);
75
76 /*
77 * visible position values
78 */
79 protected static final int XIMVisibleToForward = (1<<8);
80 protected static final int XIMVisibleToBackward = (1<<9);
81 protected static final int XIMVisibleCenter = (1<<10);
82 protected static final int XIMVisibleMask =
83 (XIMVisibleToForward | XIMVisibleToBackward | XIMVisibleCenter);
84
85 private Locale locale;
86 private static boolean isXIMOpened = false;
87 protected Container clientComponentWindow = null;
88 protected Component awtFocussedComponent = null;
89 protected Component lastXICFocussedComponent = null;
90 protected boolean isLastXICActive = false;
91 protected boolean isLastTemporary = false;
92 protected boolean isActive = false;
93 private static Map<TextAttribute, ?>[] highlightStyles;
94 protected boolean disposed = false;
95
96 //reset the XIC if necessary
97 protected boolean needResetXIC = false;
98 private WeakReference<Component> needResetXICClient = new WeakReference<>(null);
99
100 // The use of compositionEnableSupported is to reduce unnecessary
101 // native calls if set/isCompositionEnabled
102 // throws UnsupportedOperationException.
103 // It is set to false if that exception is thrown first time
104 // either of the two methods are called.
105 protected boolean compositionEnableSupported = true;
106 // The savedCompositionState indicates the composition mode when
107 // endComposition or setCompositionEnabled is called. It doesn't always
108 // reflect the actual composition state because it doesn't get updated
109 // when the user changes the composition state through direct interaction
110 // with the input method. It is used to save the composition mode when
111 // focus is traversed across different client components sharing the
112 // same java input context. Also if set/isCompositionEnabled are not
113 // supported, it remains false.
114 protected boolean savedCompositionState = false;
115
116 // variables to keep track of preedit context.
117 // these variables need to be accessed within AWT_LOCK/UNLOCK
118 protected String committedText = null;
119 protected StringBuffer composedText = null;
120 protected IntBuffer rawFeedbacks;
121
122 // private data (X11InputMethodData structure defined in
123 // awt_InputMethod.c) for native methods
124 // this structure needs to be accessed within AWT_LOCK/UNLOCK
125 protected transient long pData = 0; // accessed by native
126
127 // Initialize highlight mapping table
128 static {
129 @SuppressWarnings({"unchecked", "rawtypes"})
130 Map<TextAttribute, ?> styles[] = new Map[4];
131 HashMap<TextAttribute, Object> map;
132
133 // UNSELECTED_RAW_TEXT_HIGHLIGHT
134 map = new HashMap<>(1);
135 map.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
136 styles[0] = Collections.unmodifiableMap(map);
137
138 // SELECTED_RAW_TEXT_HIGHLIGHT
139 map = new HashMap<>(1);
140 map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
141 styles[1] = Collections.unmodifiableMap(map);
142
143 // UNSELECTED_CONVERTED_TEXT_HIGHLIGHT
144 map = new HashMap<>(1);
145 map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
146 TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
147 styles[2] = Collections.unmodifiableMap(map);
148
149 // SELECTED_CONVERTED_TEXT_HIGHLIGHT
150 map = new HashMap<>(1);
151 map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
152 styles[3] = Collections.unmodifiableMap(map);
153
154 highlightStyles = styles;
155 }
156
157 static {
158 initIDs();
159 }
160
161 /**
162 * Constructs an X11InputMethod instance. It initializes the XIM
163 * environment if it's not done yet.
164 *
165 * @exception AWTException if XOpenIM() failed.
166 */
167 public X11InputMethodBase() throws AWTException {
168 // supports only the locale in which the VM is started
169 locale = X11InputMethodDescriptor.getSupportedLocale();
170 if (initXIM() == false) {
171 throw new AWTException("Cannot open X Input Method");
172 }
173 }
174
175 @SuppressWarnings("deprecation")
176 protected void finalize() throws Throwable {
177 dispose();
178 super.finalize();
179 }
180
181 /**
182 * Invokes openIM() that invokes XOpenIM() if it's not opened yet.
183 * @return true if openXIM() is successful or it's already been opened.
184 */
185 private synchronized boolean initXIM() {
186 if (isXIMOpened == false)
187 isXIMOpened = openXIM();
232 }
233
234 /**
235 * Does nothing - XIM doesn't let you specify which characters you expect.
236 *
237 * @see java.awt.im.spi.InputMethod#setCharacterSubsets
238 */
239 public void setCharacterSubsets(Subset[] subsets) {
240 }
241
242 /**
243 * Dispatch event to input method. InputContext dispatch event with this
244 * method. Input method set consume flag if event is consumed in
245 * input method.
246 *
247 * @param e event
248 */
249 public void dispatchEvent(AWTEvent e) {
250 }
251
252 protected final void resetXICifneeded(){
253 /* needResetXIC is used to indicate whether to call
254 resetXIC on the active client. resetXIC will always be
255 called on the passive client when endComposition is called.
256 */
257 if (needResetXIC && haveActiveClient() &&
258 getClientComponent() != needResetXICClient.get()){
259 resetXIC();
260
261 // needs to reset the last xic focussed component.
262 lastXICFocussedComponent = null;
263 isLastXICActive = false;
264
265 needResetXICClient.clear();
266 needResetXIC = false;
267 }
268 }
269
270 /**
271 * Reset the composition state to the current composition state.
272 */
273 protected abstract void resetCompositionState();
274
275 /**
276 * Query and then return the current composition state.
277 * @return the composition state if isCompositionEnabled call
278 * is successful. Otherwise, it returns false.
279 */
280 protected boolean getCompositionState() {
281 boolean compositionState = false;
282 if (compositionEnableSupported) {
283 try {
284 compositionState = isCompositionEnabled();
285 } catch (UnsupportedOperationException e) {
286 compositionEnableSupported = false;
287 }
288 }
289 return compositionState;
290 }
291
292 /**
293 * Activate input method.
294 */
295 public abstract void activate();
296
297 protected abstract boolean createXIC();
298
299 /**
300 * Deactivate input method.
301 */
302 public abstract void deactivate(boolean isTemporary);
303
304 /**
305 * Explicitly disable the native IME. Native IME is not disabled when
306 * deactivate is called.
307 */
308 public void disableInputMethod() {
309 if (lastXICFocussedComponent != null) {
310 setXICFocus(getPeer(lastXICFocussedComponent), false, isLastXICActive);
311 lastXICFocussedComponent = null;
312 isLastXICActive = false;
313
314 resetXIC();
315 needResetXICClient.clear();
316 needResetXIC = false;
317 }
318 }
319
320 // implements java.awt.im.spi.InputMethod.hideWindows
321 public abstract void hideWindows();
322
323 /**
324 * @see java.awt.Toolkit#mapInputMethodHighlight
325 */
326 public static Map<TextAttribute, ?> mapInputMethodHighlight(InputMethodHighlight highlight) {
327 int index;
328 int state = highlight.getState();
329 if (state == InputMethodHighlight.RAW_TEXT) {
330 index = 0;
331 } else if (state == InputMethodHighlight.CONVERTED_TEXT) {
332 index = 2;
333 } else {
334 return null;
335 }
336 if (highlight.isSelected()) {
337 index += 1;
338 }
339 return highlightStyles[index];
340 }
341
365 // Solaris 7 do not implement this correctly without a patch,
366 // so just call resetXIC here. Prior endComposition call commits
367 // the existing composed text.
368 endComposition();
369 // disable the native input method so that the other input
370 // method could get the input focus.
371 disableInputMethod();
372 if (needResetXIC) {
373 resetXIC();
374 needResetXICClient.clear();
375 needResetXIC = false;
376 }
377 }
378
379 /**
380 * Returns the Window instance in which the client component is
381 * contained. If not found, null is returned. (IS THIS POSSIBLE?)
382 */
383 // NOTE: This method may be called by privileged threads.
384 // DO NOT INVOKE CLIENT CODE ON THIS THREAD!
385 protected Window getClientComponentWindow() {
386 Component client = getClientComponent();
387 Container container;
388
389 if (client instanceof Container) {
390 container = (Container) client;
391 } else {
392 container = getParent(client);
393 }
394
395 while (container != null && !(container instanceof java.awt.Window)) {
396 container = getParent(container);
397 }
398 return (Window) container;
399 }
400
401 protected abstract Container getParent(Component client);
402
403 /**
404 * Returns peer of the given client component. If the given client component
405 * doesn't have peer, peer of the native container of the client is returned.
406 */
407 protected abstract ComponentPeer getPeer(Component client);
408
409 /**
410 * Used to protect preedit data
411 */
412 protected abstract void awtLock();
413 protected abstract void awtUnlock();
414
415 /**
416 * Creates an input method event from the arguments given
417 * and posts it on the AWT event queue. For arguments,
418 * see InputMethodEvent. Called by input method.
419 *
420 * @see java.awt.event.InputMethodEvent#InputMethodEvent
421 */
422 protected void postInputMethodEvent(int id,
423 AttributedCharacterIterator text,
424 int committedCharacterCount,
425 TextHitInfo caret,
426 TextHitInfo visiblePosition,
427 long when) {
428 Component source = getClientComponent();
429 if (source != null) {
430 InputMethodEvent event = new InputMethodEvent(source,
431 id, when, text, committedCharacterCount, caret, visiblePosition);
432 SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event);
433 }
434 }
435
436 private void postInputMethodEvent(int id,
437 AttributedCharacterIterator text,
438 int committedCharacterCount,
439 TextHitInfo caret,
440 TextHitInfo visiblePosition) {
441 postInputMethodEvent(id, text, committedCharacterCount,
442 caret, visiblePosition, EventQueue.getMostRecentEventTime());
472 }
473 }
474
475 private void dispatchCommittedText(String str) {
476 dispatchCommittedText(str, EventQueue.getMostRecentEventTime());
477 }
478
479 /**
480 * Updates composed text with XIM preedit information and
481 * posts composed text to the awt event queue. The args of
482 * this method correspond to the XIM preedit callback
483 * information. The XIM highlight attributes are translated via
484 * fixed mapping (i.e., independent from any underlying input
485 * method engine). This method is invoked in the AWT Toolkit
486 * (X event loop) thread context and thus inside the AWT Lock.
487 */
488 // NOTE: This method may be called by privileged threads.
489 // This functionality is implemented in a package-private method
490 // to insure that it cannot be overridden by client subclasses.
491 // DO NOT INVOKE CLIENT CODE ON THIS THREAD!
492 abstract void dispatchComposedText(String chgText,
493 int chgStyles[],
494 int chgOffset,
495 int chgLength,
496 int caretPosition,
497 long when);
498
499 /**
500 * Flushes composed and committed text held in this context.
501 * This method is invoked in the AWT Toolkit (X event loop) thread context
502 * and thus inside the AWT Lock.
503 */
504 // NOTE: This method may be called by privileged threads.
505 // This functionality is implemented in a package-private method
506 // to insure that it cannot be overridden by client subclasses.
507 // DO NOT INVOKE CLIENT CODE ON THIS THREAD!
508 void flushText() {
509 String flush = (committedText != null ? committedText : "");
510 if (composedText != null) {
511 flush += composedText.toString();
512 }
513
514 if (!flush.equals("")) {
515 AttributedString attrstr = new AttributedString(flush);
516 postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
517 attrstr.getIterator(),
518 flush.length(),
519 null,
520 null,
521 EventQueue.getMostRecentEventTime());
522 composedText = null;
523 committedText = null;
524 }
525 }
526
527 /*
528 * Subclasses should override disposeImpl() instead of dispose(). Client
529 * code should always invoke dispose(), never disposeImpl().
530 */
531 protected abstract void disposeImpl();
532
533 /**
534 * Frees all X Window resources associated with this object.
535 *
536 * @see java.awt.im.spi.InputMethod#dispose
537 */
538 public final void dispose() {
539 boolean call_disposeImpl = false;
540
541 if (!disposed) {
542 synchronized (this) {
543 if (!disposed) {
544 disposed = call_disposeImpl = true;
545 }
546 }
547 }
548
549 if (call_disposeImpl) {
550 disposeImpl();
551 }
553
554 /**
555 * Returns null.
556 *
557 * @see java.awt.im.spi.InputMethod#getControlObject
558 */
559 public Object getControlObject() {
560 return null;
561 }
562
563 /**
564 * @see java.awt.im.spi.InputMethod#removeNotify
565 */
566 public synchronized void removeNotify() {
567 dispose();
568 }
569
570 /**
571 * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
572 */
573 public abstract void setCompositionEnabled(boolean enable);
574
575 /**
576 * @see java.awt.im.spi.InputMethod#isCompositionEnabled
577 */
578 public boolean isCompositionEnabled() {
579 /* isCompositionEnabledNative may throw UnsupportedOperationException.
580 Don't try to catch it since this method may be called by clients.
581 Use package private method 'getCompositionState' if you want the
582 exception to be caught.
583 */
584 return isCompositionEnabledNative();
585 }
586
587 /**
588 * Ends any input composition that may currently be going on in this
589 * context. Depending on the platform and possibly user preferences,
590 * this may commit or delete uncommitted text. Any changes to the text
591 * are communicated to the active component using an input method event.
592 *
593 * <p>
609 if (active && composedText == null && committedText == null){
610 needResetXIC = true;
611 needResetXICClient = new WeakReference<>(getClientComponent());
612 return;
613 }
614
615 String text = resetXIC();
616 /* needResetXIC is only set to true for active client. So passive
617 client should not reset the flag to false. */
618 if (active) {
619 needResetXIC = false;
620 }
621
622 // Remove any existing composed text by posting an InputMethodEvent
623 // with null composed text. It would be desirable to wait for a
624 // dispatchComposedText call from X input method engine, but some
625 // input method does not conform to the XIM specification and does
626 // not call the preedit callback to erase preedit text on calling
627 // XmbResetIC. To work around this problem, do it here by ourselves.
628 awtLock();
629 try {
630 composedText = null;
631 postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
632 null,
633 0,
634 null,
635 null);
636
637 if (text != null && text.length() > 0) {
638 dispatchCommittedText(text);
639 }
640 } finally {
641 // Put awtUnlock into finally block in case an exception is thrown.
642 awtUnlock();
643 }
644
645 // Restore the preedit state if it was enabled
646 if (savedCompositionState) {
647 resetCompositionState();
648 }
649 }
650
651 /**
652 * Returns a string with information about the current input method server, or null.
653 * On both Linux & SunOS, the value of environment variable XMODIFIERS is
654 * returned if set. Otherwise, on SunOS, $HOME/.dtprofile will be parsed
655 * to find out the language service engine (atok or wnn) since there is
656 * no API in Xlib which returns the information of native
657 * IM server or language service and we want to try our best to return as much
658 * information as possible.
659 *
660 * Note: This method could return null on Linux if XMODIFIERS is not set properly or
661 * if any IOException is thrown.
662 * See man page of XSetLocaleModifiers(3X11) for the usgae of XMODIFIERS,
663 * atok12setup(1) and wnn6setup(1) for the information written to
697 }
698
699 br.close();
700 } catch(IOException ioex) {
701 // Since this method is provided for internal testing only,
702 // we dump the stack trace for the ease of debugging.
703 ioex.printStackTrace();
704 }
705
706 imInfo = "htt " + languageEngineInfo;
707 }
708
709 return imInfo;
710 }
711
712
713 /**
714 * Performs mapping from an XIM visible feedback value to Java IM highlight.
715 * @return Java input method highlight
716 */
717 protected InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) {
718 InputMethodHighlight highlight;
719
720 switch (feedback) {
721 case XIMUnderline:
722 highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
723 break;
724 case XIMReverse:
725 highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
726 break;
727 case XIMHighlight:
728 highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
729 break;
730 case 0: //None of the value is set by Wnn
731 case XIMPrimary:
732 highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
733 break;
734 case XIMSecondary:
735 highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
736 break;
737 case XIMTertiary:
738 highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
739 break;
740 default:
741 highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
742 break;
743 }
744 return highlight;
745 }
746
747 // initial capacity size for string buffer, etc.
748 protected static final int INITIAL_SIZE = 64;
749
750 /**
751 * IntBuffer is an inner class that manipulates an int array and
752 * provides UNIX file io stream-like programming interfaces to
753 * access it. (An alternative would be to use ArrayList which may
754 * be too expensive for the work.)
755 */
756 protected final class IntBuffer {
757 private int[] intArray;
758 private int size;
759 private int index;
760
761 IntBuffer(int initialCapacity) {
762 intArray = new int[initialCapacity];
763 size = 0;
764 index = 0;
765 }
766
767 void insert(int offset, int[] values) {
768 int newSize = size + values.length;
769 if (intArray.length < newSize) {
770 int[] newIntArray = new int[newSize * 2];
771 System.arraycopy(intArray, 0, newIntArray, 0, size);
772 intArray = newIntArray;
773 }
774 System.arraycopy(intArray, offset, intArray, offset+values.length,
775 size - offset);
776 System.arraycopy(values, 0, intArray, offset, values.length);
813 }
814
815 int getOffset() {
816 return index;
817 }
818
819 public String toString() {
820 StringBuffer s = new StringBuffer();
821 for (int i = 0; i < size;) {
822 s.append(intArray[i++]);
823 if (i < size)
824 s.append(",");
825 }
826 return s.toString();
827 }
828 }
829
830 /*
831 * Native methods
832 */
833
834 /**
835 * Initialize JNI field and method IDs for fields that may be
836 * accessed from C.
837 */
838 private static native void initIDs();
839
840 protected native void turnoffStatusWindow();
841
842 protected native void disposeXIC();
843
844 private native String resetXIC();
845
846 protected native boolean setCompositionEnabledNative(boolean enable);
847
848 private native boolean isCompositionEnabledNative();
849 }
|