1 /*
2  * Copyright (c) 1997, 2021, 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);
map.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD)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);
map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON)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);
map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL)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);
map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON)151         map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
152         styles[3] = Collections.unmodifiableMap(map);
153 
154         highlightStyles = styles;
155     }
156 
157     static {
initIDs()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      */
X11InputMethodBase()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")
finalize()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      */
initXIM()185     private synchronized boolean initXIM() {
186         if (isXIMOpened == false)
187             isXIMOpened = openXIM();
188         return isXIMOpened;
189     }
190 
openXIM()191     protected abstract boolean openXIM();
192 
isDisposed()193     protected boolean isDisposed() {
194         return disposed;
195     }
196 
setXICFocus(ComponentPeer peer, boolean value, boolean active)197     protected abstract void setXICFocus(ComponentPeer peer,
198                                     boolean value, boolean active);
199 
200     /**
201      * Does nothing - this adapter doesn't use the input method context.
202      *
203      * @see java.awt.im.spi.InputMethod#setInputMethodContext
204      */
setInputMethodContext(InputMethodContext context)205     public void setInputMethodContext(InputMethodContext context) {
206     }
207 
208     /**
209      * Set locale to input. If input method doesn't support specified locale,
210      * false will be returned and its behavior is not changed.
211      *
212      * @param lang locale to input
213      * @return the true is returned when specified locale is supported.
214      */
setLocale(Locale lang)215     public boolean setLocale(Locale lang) {
216         if (lang.equals(locale)) {
217             return true;
218         }
219         // special compatibility rule for Japanese and Korean
220         if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||
221                 locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {
222             return true;
223         }
224         return false;
225     }
226 
227     /**
228      * Returns current input locale.
229      */
getLocale()230     public Locale getLocale() {
231         return locale;
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      */
setCharacterSubsets(Subset[] subsets)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      */
dispatchEvent(AWTEvent e)249     public void dispatchEvent(AWTEvent e) {
250     }
251 
resetXICifneeded()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      */
resetCompositionState()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      */
getCompositionState()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      */
activate()295     public abstract void activate();
296 
createXIC()297     protected abstract boolean createXIC();
298 
299     /**
300      * Deactivate input method.
301      */
deactivate(boolean isTemporary)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      */
disableInputMethod()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
hideWindows()321     public abstract void hideWindows();
322 
323     /**
324      * @see java.awt.Toolkit#mapInputMethodHighlight
325      */
mapInputMethodHighlight(InputMethodHighlight highlight)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 
342     /**
343      * @see sun.awt.im.InputMethodAdapter#setAWTFocussedComponent
344      */
setAWTFocussedComponent(Component component)345     protected void setAWTFocussedComponent(Component component) {
346         if (component == null) {
347             return;
348         }
349         if (isActive) {
350             // deactivate/activate are being suppressed during a focus change -
351             // this may happen when an input method window is made visible
352             boolean ac = haveActiveClient();
353             setXICFocus(getPeer(awtFocussedComponent), false, ac);
354             setXICFocus(getPeer(component), true, ac);
355         }
356         awtFocussedComponent = component;
357     }
358 
359     /**
360      * @see sun.awt.im.InputMethodAdapter#stopListening
361      */
stopListening()362     protected void stopListening() {
363         // It is desirable to disable XIM by calling XSetICValues with
364         // XNPreeditState == XIMPreeditDisable.  But Solaris 2.6 and
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!
getClientComponentWindow()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 
getParent(Component client)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      */
getPeer(Component client)407     protected abstract ComponentPeer getPeer(Component client);
408 
409     /**
410      * Used to protect preedit data
411      */
awtLock()412     protected abstract void awtLock();
awtUnlock()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      */
postInputMethodEvent(int id, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition, long when)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 
postInputMethodEvent(int id, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition)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());
443     }
444 
445     /**
446      * Dispatches committed text from XIM to the awt event queue. This
447      * method is invoked from the event handler in canvas.c in the
448      * AWT Toolkit thread context and thus inside the AWT Lock.
449      * @param   str     committed text
450      * @param   when    when
451      */
452     // NOTE: This method may be called by privileged threads.
453     //       This functionality is implemented in a package-private method
454     //       to insure that it cannot be overridden by client subclasses.
455     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
dispatchCommittedText(String str, long when)456     void dispatchCommittedText(String str, long when) {
457         if (str == null)
458             return;
459 
460         if (composedText == null) {
461             AttributedString attrstr = new AttributedString(str);
462             postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
463                                  attrstr.getIterator(),
464                                  str.length(),
465                                  null,
466                                  null,
467                                  when);
468         } else {
469             // if there is composed text, wait until the preedit
470             // callback is invoked.
471             committedText = str;
472         }
473     }
474 
dispatchCommittedText(String str)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!
dispatchComposedText(String chgText, int[] chgStyles, int chgOffset, int chgLength, int caretPosition, long when)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!
flushText()508     void flushText() {
509         String flush = (committedText != null ? committedText : "");
510         if (composedText != null) {
511             flush += composedText.toString();
512         }
513 
514         if (!flush.isEmpty()) {
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      */
disposeImpl()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      */
dispose()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         }
552     }
553 
554     /**
555      * Returns null.
556      *
557      * @see java.awt.im.spi.InputMethod#getControlObject
558      */
getControlObject()559     public Object getControlObject() {
560         return null;
561     }
562 
563     /**
564      * @see java.awt.im.spi.InputMethod#removeNotify
565      */
removeNotify()566     public synchronized void removeNotify() {
567         dispose();
568     }
569 
570     /**
571      * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
572      */
setCompositionEnabled(boolean enable)573     public abstract void setCompositionEnabled(boolean enable);
574 
575     /**
576      * @see java.awt.im.spi.InputMethod#isCompositionEnabled
577      */
isCompositionEnabled()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>
594      * A text editing component may call this in a variety of situations,
595      * for example, when the user moves the insertion point within the text
596      * (but outside the composed text), or when the component's text is
597      * saved to a file or copied to the clipboard.
598      *
599      */
endComposition()600     public void endComposition() {
601         if (disposed) {
602             return;
603         }
604 
605         /* Before calling resetXIC, record the current composition mode
606            so that it can be restored later. */
607         savedCompositionState = getCompositionState();
608         boolean active = haveActiveClient();
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
664      * $HOME/.dtprofile when you run these two commands.
665      *
666      */
getNativeInputMethodInfo()667     public String getNativeInputMethodInfo() {
668         String xmodifiers = System.getenv("XMODIFIERS");
669         String imInfo = null;
670 
671         // If XMODIFIERS is set, return the value
672         if (xmodifiers != null) {
673             int imIndex = xmodifiers.indexOf("@im=");
674             if (imIndex != -1) {
675                 imInfo = xmodifiers.substring(imIndex + 4);
676             }
677         }
678 
679         return imInfo;
680     }
681 
682 
683     /**
684      * Performs mapping from an XIM visible feedback value to Java IM highlight.
685      * @return Java input method highlight
686      */
convertVisualFeedbackToHighlight(int feedback)687     protected InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) {
688         InputMethodHighlight highlight;
689 
690         switch (feedback) {
691         case XIMUnderline:
692             highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
693             break;
694         case XIMReverse:
695             highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
696             break;
697         case XIMHighlight:
698             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
699             break;
700         case 0: //None of the values are set by Wnn
701         case XIMPrimary:
702             highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
703             break;
704         case XIMSecondary:
705             highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
706             break;
707         case XIMTertiary:
708             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
709             break;
710         default:
711             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
712             break;
713         }
714         return highlight;
715     }
716 
717     // initial capacity size for string buffer, etc.
718     protected static final int INITIAL_SIZE = 64;
719 
720     /**
721      * IntBuffer is an inner class that manipulates an int array and
722      * provides UNIX file io stream-like programming interfaces to
723      * access it. (An alternative would be to use ArrayList which may
724      * be too expensive for the work.)
725      */
726     protected final class IntBuffer {
727         private int[] intArray;
728         private int size;
729         private int index;
730 
IntBuffer(int initialCapacity)731         IntBuffer(int initialCapacity) {
732             intArray = new int[initialCapacity];
733             size = 0;
734             index = 0;
735         }
736 
insert(int offset, int[] values)737         void insert(int offset, int[] values) {
738             int newSize = size + values.length;
739             if (intArray.length < newSize) {
740                 int[] newIntArray = new int[newSize * 2];
741                 System.arraycopy(intArray, 0, newIntArray, 0, size);
742                 intArray = newIntArray;
743             }
744             System.arraycopy(intArray, offset, intArray, offset+values.length,
745                              size - offset);
746             System.arraycopy(values, 0, intArray, offset, values.length);
747             size += values.length;
748             if (index > offset)
749                 index = offset;
750         }
751 
remove(int offset, int length)752         void remove(int offset, int length) {
753             if (offset + length != size)
754                 System.arraycopy(intArray, offset+length, intArray, offset,
755                                  size - offset - length);
756             size -= length;
757             if (index > offset)
758                 index = offset;
759         }
760 
replace(int offset, int[] values)761         void replace(int offset, int[] values) {
762             System.arraycopy(values, 0, intArray, offset, values.length);
763         }
764 
removeAll()765         void removeAll() {
766             size = 0;
767             index = 0;
768         }
769 
rewind()770         void rewind() {
771             index = 0;
772         }
773 
getNext()774         int getNext() {
775             if (index == size)
776                 return -1;
777             return intArray[index++];
778         }
779 
unget()780         void unget() {
781             if (index != 0)
782                 index--;
783         }
784 
getOffset()785         int getOffset() {
786             return index;
787         }
788 
toString()789         public String toString() {
790             StringBuilder s = new StringBuilder();
791             for (int i = 0; i < size;) {
792                 s.append(intArray[i++]);
793                 if (i < size)
794                     s.append(",");
795             }
796             return s.toString();
797         }
798     }
799 
800     /*
801      * Native methods
802      */
803 
804     /**
805      * Initialize JNI field and method IDs for fields that may be
806      * accessed from C.
807      */
initIDs()808     private static native void initIDs();
809 
turnoffStatusWindow()810     protected native void turnoffStatusWindow();
811 
disposeXIC()812     protected native void disposeXIC();
813 
resetXIC()814     private native String resetXIC();
815 
setCompositionEnabledNative(boolean enable)816     protected native boolean setCompositionEnabledNative(boolean enable);
817 
isCompositionEnabledNative()818     private native boolean isCompositionEnabledNative();
819 }
820