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 
27 package sun.awt.windows;
28 
29 import java.awt.*;
30 import java.awt.peer.*;
31 import java.awt.event.*;
32 import java.awt.im.*;
33 import java.awt.im.spi.InputMethodContext;
34 import java.awt.font.*;
35 import java.text.*;
36 import java.text.AttributedCharacterIterator.Attribute;
37 import java.lang.Character.Subset;
38 import java.lang.Character.UnicodeBlock;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.Locale;
42 import java.util.Map;
43 
44 import sun.awt.AWTAccessor;
45 import sun.awt.AWTAccessor.ComponentAccessor;
46 import sun.awt.im.InputMethodAdapter;
47 
48 final class WInputMethod extends InputMethodAdapter
49 {
50     /**
51      * The input method context, which is used to dispatch input method
52      * events to the client component and to request information from
53      * the client component.
54      */
55     private InputMethodContext inputContext;
56 
57     private Component awtFocussedComponent;
58     private WComponentPeer awtFocussedComponentPeer = null;
59     private WComponentPeer lastFocussedComponentPeer = null;
60     private boolean isLastFocussedActiveClient = false;
61     private boolean isActive;
62     private int context;
63     private boolean open; //default open status;
64     private int cmode;    //default conversion mode;
65     private Locale currentLocale;
66     // indicate whether status window is hidden or not.
67     private boolean statusWindowHidden = false;
68     private boolean hasCompositionString = false;
69 
70     // attribute definition in Win32 (in IMM.H)
71     public static final byte ATTR_INPUT                 = 0x00;
72     public static final byte ATTR_TARGET_CONVERTED      = 0x01;
73     public static final byte ATTR_CONVERTED             = 0x02;
74     public static final byte ATTR_TARGET_NOTCONVERTED   = 0x03;
75     public static final byte ATTR_INPUT_ERROR           = 0x04;
76     // cmode definition in Win32 (in IMM.H)
77     public static final int  IME_CMODE_ALPHANUMERIC     = 0x0000;
78     public static final int  IME_CMODE_NATIVE           = 0x0001;
79     public static final int  IME_CMODE_KATAKANA         = 0x0002;
80     public static final int  IME_CMODE_LANGUAGE         = 0x0003;
81     public static final int  IME_CMODE_FULLSHAPE        = 0x0008;
82     public static final int  IME_CMODE_HANJACONVERT     = 0x0040;
83     public static final int  IME_CMODE_ROMAN            = 0x0010;
84 
85     // flag values for endCompositionNative() behavior
86     private static final boolean COMMIT_INPUT           = true;
87     private static final boolean DISCARD_INPUT          = false;
88 
89     private static Map<TextAttribute,Object> [] highlightStyles;
90 
91     // Initialize highlight mapping table
92     static {
93         @SuppressWarnings({"rawtypes", "unchecked"})
94         Map<TextAttribute,Object> styles[] = new Map[4];
95         HashMap<TextAttribute,Object> map;
96 
97         // UNSELECTED_RAW_TEXT_HIGHLIGHT
98         map = new HashMap<>(1);
map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_DOTTED)99         map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_DOTTED);
100         styles[0] = Collections.unmodifiableMap(map);
101 
102         // SELECTED_RAW_TEXT_HIGHLIGHT
103         map = new HashMap<>(1);
map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_GRAY)104         map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_GRAY);
105         styles[1] = Collections.unmodifiableMap(map);
106 
107         // UNSELECTED_CONVERTED_TEXT_HIGHLIGHT
108         map = new HashMap<>(1);
map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_DOTTED)109         map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_DOTTED);
110         styles[2] = Collections.unmodifiableMap(map);
111 
112         // SELECTED_CONVERTED_TEXT_HIGHLIGHT
113         map = new HashMap<>(4);
114         Color navyBlue = new Color(0, 0, 128);
map.put(TextAttribute.FOREGROUND, navyBlue)115         map.put(TextAttribute.FOREGROUND, navyBlue);
map.put(TextAttribute.BACKGROUND, Color.white)116         map.put(TextAttribute.BACKGROUND, Color.white);
map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON)117         map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL)118         map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
119         styles[3] = Collections.unmodifiableMap(map);
120 
121         highlightStyles = styles;
122     }
123 
WInputMethod()124     public WInputMethod()
125     {
126         context = createNativeContext();
127         cmode = getConversionStatus(context);
128         open = getOpenStatus(context);
129         currentLocale = getNativeLocale();
130         if (currentLocale == null) {
131             currentLocale = Locale.getDefault();
132         }
133     }
134 
135     @Override
136     @SuppressWarnings("deprecation")
finalize()137     protected void finalize() throws Throwable
138     {
139         // Release the resources used by the native input context.
140         if (context!=0) {
141             destroyNativeContext(context);
142             context=0;
143         }
144         super.finalize();
145     }
146 
147     @Override
setInputMethodContext(InputMethodContext context)148     public synchronized void setInputMethodContext(InputMethodContext context) {
149         inputContext = context;
150     }
151 
152     @Override
dispose()153     public void dispose() {
154         // Due to a memory management problem in Windows 98, we should retain
155         // the native input context until this object is finalized. So do
156         // nothing here.
157     }
158 
159     /**
160      * Returns null.
161      *
162      * @see java.awt.im.spi.InputMethod#getControlObject
163      */
164     @Override
getControlObject()165     public Object getControlObject() {
166         return null;
167     }
168 
169     @Override
setLocale(Locale lang)170     public boolean setLocale(Locale lang) {
171         return setLocale(lang, false);
172     }
173 
setLocale(Locale lang, boolean onActivate)174     private boolean setLocale(Locale lang, boolean onActivate) {
175         Locale[] available = WInputMethodDescriptor.getAvailableLocalesInternal();
176         for (int i = 0; i < available.length; i++) {
177             Locale locale = available[i];
178             if (lang.equals(locale) ||
179                     // special compatibility rule for Japanese and Korean
180                     locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||
181                     locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {
182                 if (isActive) {
183                     setNativeLocale(locale.toLanguageTag(), onActivate);
184                 }
185                 currentLocale = locale;
186                 return true;
187             }
188         }
189         return false;
190     }
191 
192     @Override
getLocale()193     public Locale getLocale() {
194         if (isActive) {
195             currentLocale = getNativeLocale();
196             if (currentLocale == null) {
197                 currentLocale = Locale.getDefault();
198             }
199         }
200         return currentLocale;
201     }
202 
203     /**
204      * Implements InputMethod.setCharacterSubsets for Windows.
205      *
206      * @see java.awt.im.spi.InputMethod#setCharacterSubsets
207      */
208     @Override
setCharacterSubsets(Subset[] subsets)209     public void setCharacterSubsets(Subset[] subsets) {
210         if (subsets == null){
211             setConversionStatus(context, cmode);
212             setOpenStatus(context, open);
213             return;
214         }
215 
216         // Use first subset only. Other subsets in array is ignored.
217         // This is restriction of Win32 implementation.
218         Subset subset1 = subsets[0];
219 
220         Locale locale = getNativeLocale();
221         int newmode;
222 
223         if (locale == null) {
224             return;
225         }
226 
227         if (locale.getLanguage().equals(Locale.JAPANESE.getLanguage())) {
228             if (subset1 == UnicodeBlock.BASIC_LATIN || subset1 == InputSubset.LATIN_DIGITS) {
229                 setOpenStatus(context, false);
230             } else {
231                 if (subset1 == UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
232                     || subset1 == InputSubset.KANJI
233                     || subset1 == UnicodeBlock.HIRAGANA)
234                     newmode = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE;
235                 else if (subset1 == UnicodeBlock.KATAKANA)
236                     newmode = IME_CMODE_NATIVE | IME_CMODE_KATAKANA| IME_CMODE_FULLSHAPE;
237                 else if (subset1 == InputSubset.HALFWIDTH_KATAKANA)
238                     newmode = IME_CMODE_NATIVE | IME_CMODE_KATAKANA;
239                 else if (subset1 == InputSubset.FULLWIDTH_LATIN)
240                     newmode = IME_CMODE_FULLSHAPE;
241                 else
242                     return;
243                 setOpenStatus(context, true);
244                 newmode |= (getConversionStatus(context)&IME_CMODE_ROMAN);   // reserve ROMAN input mode
245                 setConversionStatus(context, newmode);
246             }
247         } else if (locale.getLanguage().equals(Locale.KOREAN.getLanguage())) {
248             if (subset1 == UnicodeBlock.BASIC_LATIN || subset1 == InputSubset.LATIN_DIGITS) {
249                 setOpenStatus(context, false);
250                 setConversionStatus(context, IME_CMODE_ALPHANUMERIC);
251             } else {
252                 if (subset1 == UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
253                     || subset1 == InputSubset.HANJA
254                     || subset1 == UnicodeBlock.HANGUL_SYLLABLES
255                     || subset1 == UnicodeBlock.HANGUL_JAMO
256                     || subset1 == UnicodeBlock.HANGUL_COMPATIBILITY_JAMO)
257                     newmode = IME_CMODE_NATIVE;
258                 else if (subset1 == InputSubset.FULLWIDTH_LATIN)
259                     newmode = IME_CMODE_FULLSHAPE;
260                 else
261                     return;
262                 setOpenStatus(context, true);
263                 setConversionStatus(context, newmode);
264             }
265         } else if (locale.getLanguage().equals(Locale.CHINESE.getLanguage())) {
266             if (subset1 == UnicodeBlock.BASIC_LATIN || subset1 == InputSubset.LATIN_DIGITS) {
267                 setOpenStatus(context, false);
268                 newmode = getConversionStatus(context);
269                 newmode &= ~IME_CMODE_FULLSHAPE;
270                 setConversionStatus(context, newmode);
271             } else {
272                 if (subset1 == UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
273                     || subset1 == InputSubset.TRADITIONAL_HANZI
274                     || subset1 == InputSubset.SIMPLIFIED_HANZI)
275                     newmode = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE;
276                 else if (subset1 == InputSubset.FULLWIDTH_LATIN)
277                     newmode = IME_CMODE_FULLSHAPE;
278                 else
279                     return;
280                 setOpenStatus(context, true);
281                 setConversionStatus(context, newmode);
282             }
283         }
284     }
285 
286     @Override
dispatchEvent(AWTEvent e)287     public void dispatchEvent(AWTEvent e) {
288         if (e instanceof ComponentEvent) {
289             Component comp = ((ComponentEvent) e).getComponent();
290             if (comp == awtFocussedComponent) {
291                 if (awtFocussedComponentPeer == null ||
292                     awtFocussedComponentPeer.isDisposed()) {
293                     awtFocussedComponentPeer = getNearestNativePeer(comp);
294                 }
295                 if (awtFocussedComponentPeer != null) {
296                     handleNativeIMEEvent(awtFocussedComponentPeer, e);
297                 }
298             }
299         }
300     }
301 
302     @Override
activate()303     public void activate() {
304         boolean isAc = haveActiveClient();
305 
306         // When the last focussed component peer is different from the
307         // current focussed component or if they are different client
308         // (active or passive), disable native IME for the old focussed
309         // component and enable for the new one.
310         if (lastFocussedComponentPeer != awtFocussedComponentPeer ||
311             isLastFocussedActiveClient != isAc) {
312             if (lastFocussedComponentPeer != null) {
313                 disableNativeIME(lastFocussedComponentPeer);
314             }
315             if (awtFocussedComponentPeer != null) {
316                 enableNativeIME(awtFocussedComponentPeer, context, !isAc);
317             }
318             lastFocussedComponentPeer = awtFocussedComponentPeer;
319             isLastFocussedActiveClient = isAc;
320         }
321         isActive = true;
322         if (currentLocale != null) {
323             setLocale(currentLocale, true);
324         }
325 
326         // Compare IM's composition string with Java's composition string
327         if (hasCompositionString && !isCompositionStringAvailable(context)) {
328             endCompositionNative(context, DISCARD_INPUT);
329             sendInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
330                 EventQueue.getMostRecentEventTime(),
331                 null, null, null, null, null, 0, 0, 0);
332             hasCompositionString = false;
333         }
334 
335         /* If the status window or Windows language bar is turned off due to
336            native input method was switched to java input method, we
337            have to turn it on otherwise it is gone for good until next time
338            the user turns it on through Windows Control Panel. See details
339            from bug 6252674.
340         */
341         if (statusWindowHidden) {
342             setStatusWindowVisible(awtFocussedComponentPeer, true);
343             statusWindowHidden = false;
344         }
345 
346     }
347 
348     @Override
deactivate(boolean isTemporary)349     public void deactivate(boolean isTemporary)
350     {
351         // Sync currentLocale with the Windows keyboard layout which might be changed
352         // by hot key
353         getLocale();
354 
355         // Delay calling disableNativeIME until activate is called and the newly
356         // focussed component has a different peer as the last focussed component.
357         if (awtFocussedComponentPeer != null) {
358             lastFocussedComponentPeer = awtFocussedComponentPeer;
359             isLastFocussedActiveClient = haveActiveClient();
360         }
361         isActive = false;
362         hasCompositionString = isCompositionStringAvailable(context);
363 
364         // IME is going to be disabled commit the composition string
365         if (hasCompositionString) {
366             endComposition();
367         }
368     }
369 
370     /**
371      * Explicitly disable the native IME. Native IME is not disabled when
372      * deactivate is called.
373      */
374     @Override
disableInputMethod()375     public void disableInputMethod() {
376         if (lastFocussedComponentPeer != null) {
377             disableNativeIME(lastFocussedComponentPeer);
378             lastFocussedComponentPeer = null;
379             isLastFocussedActiveClient = false;
380         }
381     }
382 
383     /**
384      * Returns a string with information about the windows input method,
385      * or null.
386      */
387     @Override
getNativeInputMethodInfo()388     public String getNativeInputMethodInfo() {
389         return getNativeIMMDescription();
390     }
391 
392      /**
393      * @see sun.awt.im.InputMethodAdapter#stopListening
394      * This method is called when the input method is swapped out.
395      * Calling stopListening to give other input method the keybaord input
396      * focus.
397      */
398     @Override
stopListening()399     protected void stopListening() {
400         // Since the native input method is not disabled when deactivate is
401         // called, we need to call disableInputMethod to explicitly turn off the
402         // native IME.
403         disableInputMethod();
404     }
405 
406     // implements sun.awt.im.InputMethodAdapter.setAWTFocussedComponent
407     @Override
setAWTFocussedComponent(Component component)408     protected void setAWTFocussedComponent(Component component) {
409         if (component == null) {
410             return;
411         }
412         WComponentPeer peer = getNearestNativePeer(component);
413         if (isActive) {
414             // deactivate/activate are being suppressed during a focus change -
415             // this may happen when an input method window is made visible
416             if (awtFocussedComponentPeer != null) {
417                 disableNativeIME(awtFocussedComponentPeer);
418             }
419             if (peer != null) {
420                 enableNativeIME(peer, context, !haveActiveClient());
421             }
422         }
423         awtFocussedComponent = component;
424         awtFocussedComponentPeer = peer;
425     }
426 
427     // implements java.awt.im.spi.InputMethod.hideWindows
428     @Override
hideWindows()429     public void hideWindows() {
430         if (awtFocussedComponentPeer != null) {
431             /* Hide the native status window including the Windows language
432                bar if it is on. One typical senario this method
433                gets called is when the native input method is
434                switched to java input method, for example.
435             */
436             setStatusWindowVisible(awtFocussedComponentPeer, false);
437             statusWindowHidden = true;
438         }
439     }
440 
441     /**
442      * @see java.awt.im.spi.InputMethod#removeNotify
443      */
444     @Override
removeNotify()445     public void removeNotify() {
446         endCompositionNative(context, DISCARD_INPUT);
447         awtFocussedComponent = null;
448         awtFocussedComponentPeer = null;
449     }
450 
451     /**
452      * @see java.awt.Toolkit#mapInputMethodHighlight
453      */
mapInputMethodHighlight(InputMethodHighlight highlight)454     static Map<TextAttribute,?> mapInputMethodHighlight(InputMethodHighlight highlight) {
455         int index;
456         int state = highlight.getState();
457         if (state == InputMethodHighlight.RAW_TEXT) {
458             index = 0;
459         } else if (state == InputMethodHighlight.CONVERTED_TEXT) {
460             index = 2;
461         } else {
462             return null;
463         }
464         if (highlight.isSelected()) {
465             index += 1;
466         }
467         return highlightStyles[index];
468     }
469 
470     // see sun.awt.im.InputMethodAdapter.supportsBelowTheSpot
471     @Override
supportsBelowTheSpot()472     protected boolean supportsBelowTheSpot() {
473         return true;
474     }
475 
476     @Override
endComposition()477     public void endComposition()
478     {
479         //right now the native endCompositionNative() just cancel
480         //the composition string, maybe a commtting is desired
481         endCompositionNative(context,
482             (haveActiveClient() ? COMMIT_INPUT : DISCARD_INPUT));
483     }
484 
485     /**
486      * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
487      */
488     @Override
setCompositionEnabled(boolean enable)489     public void setCompositionEnabled(boolean enable) {
490         setOpenStatus(context, enable);
491     }
492 
493     /**
494      * @see java.awt.im.spi.InputMethod#isCompositionEnabled
495      */
496     @Override
isCompositionEnabled()497     public boolean isCompositionEnabled() {
498         return getOpenStatus(context);
499     }
500 
sendInputMethodEvent(int id, long when, String text, int[] clauseBoundary, String[] clauseReading, int[] attributeBoundary, byte[] attributeValue, int commitedTextLength, int caretPos, int visiblePos)501     public void sendInputMethodEvent(int id, long when, String text,
502                                      int[] clauseBoundary, String[] clauseReading,
503                                      int[] attributeBoundary, byte[] attributeValue,
504                                      int commitedTextLength, int caretPos, int visiblePos)
505     {
506 
507         AttributedCharacterIterator iterator = null;
508 
509         if (text!=null) {
510 
511             // construct AttributedString
512             AttributedString attrStr = new AttributedString(text);
513 
514             // set Language Information
515             attrStr.addAttribute(Attribute.LANGUAGE,
516                                             Locale.getDefault(), 0, text.length());
517 
518             // set Clause and Reading Information
519             if (clauseBoundary!=null && clauseReading!=null &&
520                 clauseReading.length!=0 && clauseBoundary.length==clauseReading.length+1 &&
521                 clauseBoundary[0]==0 && clauseBoundary[clauseReading.length]<=text.length() )
522             {
523                 for (int i=0; i<clauseBoundary.length-1; i++) {
524                     attrStr.addAttribute(Attribute.INPUT_METHOD_SEGMENT,
525                                             new Annotation(null), clauseBoundary[i], clauseBoundary[i+1]);
526                     attrStr.addAttribute(Attribute.READING,
527                                             new Annotation(clauseReading[i]), clauseBoundary[i], clauseBoundary[i+1]);
528                 }
529             } else {
530                 // if (clauseBoundary != null)
531                 //    System.out.println("Invalid clause information!");
532 
533                 attrStr.addAttribute(Attribute.INPUT_METHOD_SEGMENT,
534                                         new Annotation(null), 0, text.length());
535                 attrStr.addAttribute(Attribute.READING,
536                                      new Annotation(""), 0, text.length());
537             }
538 
539             // set Hilight Information
540             if (attributeBoundary!=null && attributeValue!=null &&
541                 attributeValue.length!=0 && attributeBoundary.length==attributeValue.length+1 &&
542                 attributeBoundary[0]==0 && attributeBoundary[attributeValue.length]==text.length() )
543             {
544                 for (int i=0; i<attributeBoundary.length-1; i++) {
545                     InputMethodHighlight highlight;
546                     switch (attributeValue[i]) {
547                         case ATTR_TARGET_CONVERTED:
548                             highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
549                             break;
550                         case ATTR_CONVERTED:
551                             highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
552                             break;
553                         case ATTR_TARGET_NOTCONVERTED:
554                             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
555                             break;
556                         case ATTR_INPUT:
557                         case ATTR_INPUT_ERROR:
558                         default:
559                             highlight = InputMethodHighlight.UNSELECTED_RAW_TEXT_HIGHLIGHT;
560                             break;
561                     }
562                     attrStr.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
563                                          highlight,
564                                          attributeBoundary[i], attributeBoundary[i+1]);
565                 }
566             } else {
567                 // if (attributeBoundary != null)
568                 //    System.out.println("Invalid attribute information!");
569 
570                 attrStr.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
571                              InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT,
572                              0, text.length());
573             }
574 
575             // get iterator
576             iterator = attrStr.getIterator();
577 
578         }
579 
580         Component source = getClientComponent();
581         if (source == null)
582             return;
583 
584         InputMethodEvent event = new InputMethodEvent(source,
585                                                       id,
586                                                       when,
587                                                       iterator,
588                                                       commitedTextLength,
589                                                       TextHitInfo.leading(caretPos),
590                                                       TextHitInfo.leading(visiblePos));
591         WToolkit.postEvent(WToolkit.targetToAppContext(source), event);
592     }
593 
inquireCandidatePosition()594     public void inquireCandidatePosition()
595     {
596         Component source = getClientComponent();
597         if (source == null) {
598             return;
599         }
600         // This call should return immediately just to cause
601         // InputMethodRequests.getTextLocation be called within
602         // AWT Event thread.  Otherwise, a potential deadlock
603         // could happen.
604         Runnable r = new Runnable() {
605             @Override
606             public void run() {
607                 int x = 0;
608                 int y = 0;
609                 Component client = getClientComponent();
610 
611                 if (client != null) {
612                     if (!client.isShowing()) {
613                         return;
614                     }
615                     if (haveActiveClient()) {
616                             Rectangle rc = inputContext.getTextLocation(TextHitInfo.leading(0));
617                             x = rc.x;
618                             y = rc.y + rc.height;
619                     } else {
620                             Point pt = client.getLocationOnScreen();
621                             Dimension size = client.getSize();
622                             x = pt.x;
623                             y = pt.y + size.height;
624                     }
625                 }
626 
627                 openCandidateWindow(awtFocussedComponentPeer, x, y);
628             }
629         };
630         WToolkit.postEvent(WToolkit.targetToAppContext(source),
631                            new InvocationEvent(source, r));
632     }
633 
634     // java.awt.Toolkit#getNativeContainer() is not available
635     //  from this package
getNearestNativePeer(Component comp)636     private WComponentPeer getNearestNativePeer(Component comp)
637     {
638         if (comp==null)     return null;
639         final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
640         ComponentPeer peer = acc.getPeer(comp);
641         if (peer==null)     return null;
642 
643         while (peer instanceof java.awt.peer.LightweightPeer) {
644             comp = comp.getParent();
645             if (comp==null) return null;
646             peer = acc.getPeer(comp);
647             if (peer==null) return null;
648         }
649 
650         if (peer instanceof WComponentPeer)
651             return (WComponentPeer)peer;
652         else
653             return null;
654 
655     }
656 
createNativeContext()657     private native int createNativeContext();
destroyNativeContext(int context)658     private native void destroyNativeContext(int context);
enableNativeIME(WComponentPeer peer, int context, boolean useNativeCompWindow)659     private native void enableNativeIME(WComponentPeer peer, int context, boolean useNativeCompWindow);
disableNativeIME(WComponentPeer peer)660     private native void disableNativeIME(WComponentPeer peer);
handleNativeIMEEvent(WComponentPeer peer, AWTEvent e)661     private native void handleNativeIMEEvent(WComponentPeer peer, AWTEvent e);
endCompositionNative(int context, boolean flag)662     private native void endCompositionNative(int context, boolean flag);
setConversionStatus(int context, int cmode)663     private native void setConversionStatus(int context, int cmode);
getConversionStatus(int context)664     private native int  getConversionStatus(int context);
setOpenStatus(int context, boolean flag)665     private native void setOpenStatus(int context, boolean flag);
getOpenStatus(int context)666     private native boolean getOpenStatus(int context);
setStatusWindowVisible(WComponentPeer peer, boolean visible)667     private native void setStatusWindowVisible(WComponentPeer peer, boolean visible);
getNativeIMMDescription()668     private native String getNativeIMMDescription();
getNativeLocale()669     static native Locale getNativeLocale();
setNativeLocale(String localeName, boolean onActivate)670     static native boolean setNativeLocale(String localeName, boolean onActivate);
openCandidateWindow(WComponentPeer peer, int x, int y)671     private native void openCandidateWindow(WComponentPeer peer, int x, int y);
isCompositionStringAvailable(int context)672     private native boolean isCompositionStringAvailable(int context);
673 }
674