1 /*
   2  * Copyright (c) 1997, 2019, 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);
  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);
 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);
 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);
 115         map.put(TextAttribute.FOREGROUND, navyBlue);
 116         map.put(TextAttribute.BACKGROUND, Color.white);
 117         map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
 118         map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
 119         styles[3] = Collections.unmodifiableMap(map);
 120 
 121         highlightStyles = styles;
 122     }
 123 
 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")
 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
 148     public synchronized void setInputMethodContext(InputMethodContext context) {
 149         inputContext = context;
 150     }
 151 
 152     @Override
 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
 165     public Object getControlObject() {
 166         return null;
 167     }
 168 
 169     @Override
 170     public boolean setLocale(Locale lang) {
 171         return setLocale(lang, false);
 172     }
 173 
 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
 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
 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
 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
 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
 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 
 365     /**
 366      * Explicitly disable the native IME. Native IME is not disabled when
 367      * deactivate is called.
 368      */
 369     @Override
 370     public void disableInputMethod() {
 371         if (lastFocussedComponentPeer != null) {
 372             disableNativeIME(lastFocussedComponentPeer);
 373             lastFocussedComponentPeer = null;
 374             isLastFocussedActiveClient = false;
 375         }
 376     }
 377 
 378     /**
 379      * Returns a string with information about the windows input method,
 380      * or null.
 381      */
 382     @Override
 383     public String getNativeInputMethodInfo() {
 384         return getNativeIMMDescription();
 385     }
 386 
 387      /**
 388      * @see sun.awt.im.InputMethodAdapter#stopListening
 389      * This method is called when the input method is swapped out.
 390      * Calling stopListening to give other input method the keybaord input
 391      * focus.
 392      */
 393     @Override
 394     protected void stopListening() {
 395         // Since the native input method is not disabled when deactivate is
 396         // called, we need to call disableInputMethod to explicitly turn off the
 397         // native IME.
 398         disableInputMethod();
 399     }
 400 
 401     // implements sun.awt.im.InputMethodAdapter.setAWTFocussedComponent
 402     @Override
 403     protected void setAWTFocussedComponent(Component component) {
 404         if (component == null) {
 405             return;
 406         }
 407         WComponentPeer peer = getNearestNativePeer(component);
 408         if (isActive) {
 409             // deactivate/activate are being suppressed during a focus change -
 410             // this may happen when an input method window is made visible
 411             if (awtFocussedComponentPeer != null) {
 412                 disableNativeIME(awtFocussedComponentPeer);
 413             }
 414             if (peer != null) {
 415                 enableNativeIME(peer, context, !haveActiveClient());
 416             }
 417         }
 418         awtFocussedComponent = component;
 419         awtFocussedComponentPeer = peer;
 420     }
 421 
 422     // implements java.awt.im.spi.InputMethod.hideWindows
 423     @Override
 424     public void hideWindows() {
 425         if (awtFocussedComponentPeer != null) {
 426             /* Hide the native status window including the Windows language
 427                bar if it is on. One typical senario this method
 428                gets called is when the native input method is
 429                switched to java input method, for example.
 430             */
 431             setStatusWindowVisible(awtFocussedComponentPeer, false);
 432             statusWindowHidden = true;
 433         }
 434     }
 435 
 436     /**
 437      * @see java.awt.im.spi.InputMethod#removeNotify
 438      */
 439     @Override
 440     public void removeNotify() {
 441         endCompositionNative(context, DISCARD_INPUT);
 442         awtFocussedComponent = null;
 443         awtFocussedComponentPeer = null;
 444     }
 445 
 446     /**
 447      * @see java.awt.Toolkit#mapInputMethodHighlight
 448      */
 449     static Map<TextAttribute,?> mapInputMethodHighlight(InputMethodHighlight highlight) {
 450         int index;
 451         int state = highlight.getState();
 452         if (state == InputMethodHighlight.RAW_TEXT) {
 453             index = 0;
 454         } else if (state == InputMethodHighlight.CONVERTED_TEXT) {
 455             index = 2;
 456         } else {
 457             return null;
 458         }
 459         if (highlight.isSelected()) {
 460             index += 1;
 461         }
 462         return highlightStyles[index];
 463     }
 464 
 465     // see sun.awt.im.InputMethodAdapter.supportsBelowTheSpot
 466     @Override
 467     protected boolean supportsBelowTheSpot() {
 468         return true;
 469     }
 470 
 471     @Override
 472     public void endComposition()
 473     {
 474         //right now the native endCompositionNative() just cancel
 475         //the composition string, maybe a commtting is desired
 476         endCompositionNative(context,
 477             (haveActiveClient() ? COMMIT_INPUT : DISCARD_INPUT));
 478     }
 479 
 480     /**
 481      * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
 482      */
 483     @Override
 484     public void setCompositionEnabled(boolean enable) {
 485         setOpenStatus(context, enable);
 486     }
 487 
 488     /**
 489      * @see java.awt.im.spi.InputMethod#isCompositionEnabled
 490      */
 491     @Override
 492     public boolean isCompositionEnabled() {
 493         return getOpenStatus(context);
 494     }
 495 
 496     public void sendInputMethodEvent(int id, long when, String text,
 497                                      int[] clauseBoundary, String[] clauseReading,
 498                                      int[] attributeBoundary, byte[] attributeValue,
 499                                      int commitedTextLength, int caretPos, int visiblePos)
 500     {
 501 
 502         AttributedCharacterIterator iterator = null;
 503 
 504         if (text!=null) {
 505 
 506             // construct AttributedString
 507             AttributedString attrStr = new AttributedString(text);
 508 
 509             // set Language Information
 510             attrStr.addAttribute(Attribute.LANGUAGE,
 511                                             Locale.getDefault(), 0, text.length());
 512 
 513             // set Clause and Reading Information
 514             if (clauseBoundary!=null && clauseReading!=null &&
 515                 clauseReading.length!=0 && clauseBoundary.length==clauseReading.length+1 &&
 516                 clauseBoundary[0]==0 && clauseBoundary[clauseReading.length]<=text.length() )
 517             {
 518                 for (int i=0; i<clauseBoundary.length-1; i++) {
 519                     attrStr.addAttribute(Attribute.INPUT_METHOD_SEGMENT,
 520                                             new Annotation(null), clauseBoundary[i], clauseBoundary[i+1]);
 521                     attrStr.addAttribute(Attribute.READING,
 522                                             new Annotation(clauseReading[i]), clauseBoundary[i], clauseBoundary[i+1]);
 523                 }
 524             } else {
 525                 // if (clauseBoundary != null)
 526                 //    System.out.println("Invalid clause information!");
 527 
 528                 attrStr.addAttribute(Attribute.INPUT_METHOD_SEGMENT,
 529                                         new Annotation(null), 0, text.length());
 530                 attrStr.addAttribute(Attribute.READING,
 531                                      new Annotation(""), 0, text.length());
 532             }
 533 
 534             // set Hilight Information
 535             if (attributeBoundary!=null && attributeValue!=null &&
 536                 attributeValue.length!=0 && attributeBoundary.length==attributeValue.length+1 &&
 537                 attributeBoundary[0]==0 && attributeBoundary[attributeValue.length]==text.length() )
 538             {
 539                 for (int i=0; i<attributeBoundary.length-1; i++) {
 540                     InputMethodHighlight highlight;
 541                     switch (attributeValue[i]) {
 542                         case ATTR_TARGET_CONVERTED:
 543                             highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
 544                             break;
 545                         case ATTR_CONVERTED:
 546                             highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
 547                             break;
 548                         case ATTR_TARGET_NOTCONVERTED:
 549                             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
 550                             break;
 551                         case ATTR_INPUT:
 552                         case ATTR_INPUT_ERROR:
 553                         default:
 554                             highlight = InputMethodHighlight.UNSELECTED_RAW_TEXT_HIGHLIGHT;
 555                             break;
 556                     }
 557                     attrStr.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
 558                                          highlight,
 559                                          attributeBoundary[i], attributeBoundary[i+1]);
 560                 }
 561             } else {
 562                 // if (attributeBoundary != null)
 563                 //    System.out.println("Invalid attribute information!");
 564 
 565                 attrStr.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
 566                              InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT,
 567                              0, text.length());
 568             }
 569 
 570             // get iterator
 571             iterator = attrStr.getIterator();
 572 
 573         }
 574 
 575         Component source = getClientComponent();
 576         if (source == null)
 577             return;
 578 
 579         InputMethodEvent event = new InputMethodEvent(source,
 580                                                       id,
 581                                                       when,
 582                                                       iterator,
 583                                                       commitedTextLength,
 584                                                       TextHitInfo.leading(caretPos),
 585                                                       TextHitInfo.leading(visiblePos));
 586         WToolkit.postEvent(WToolkit.targetToAppContext(source), event);
 587     }
 588 
 589     public void inquireCandidatePosition()
 590     {
 591         Component source = getClientComponent();
 592         if (source == null) {
 593             return;
 594         }
 595         // This call should return immediately just to cause
 596         // InputMethodRequests.getTextLocation be called within
 597         // AWT Event thread.  Otherwise, a potential deadlock
 598         // could happen.
 599         Runnable r = new Runnable() {
 600             @Override
 601             public void run() {
 602                 int x = 0;
 603                 int y = 0;
 604                 Component client = getClientComponent();
 605 
 606                 if (client != null) {
 607                     if (!client.isShowing()) {
 608                         return;
 609                     }
 610                     if (haveActiveClient()) {
 611                             Rectangle rc = inputContext.getTextLocation(TextHitInfo.leading(0));
 612                             x = rc.x;
 613                             y = rc.y + rc.height;
 614                     } else {
 615                             Point pt = client.getLocationOnScreen();
 616                             Dimension size = client.getSize();
 617                             x = pt.x;
 618                             y = pt.y + size.height;
 619                     }
 620                 }
 621 
 622                 openCandidateWindow(awtFocussedComponentPeer, x, y);
 623             }
 624         };
 625         WToolkit.postEvent(WToolkit.targetToAppContext(source),
 626                            new InvocationEvent(source, r));
 627     }
 628 
 629     // java.awt.Toolkit#getNativeContainer() is not available
 630     //  from this package
 631     private WComponentPeer getNearestNativePeer(Component comp)
 632     {
 633         if (comp==null)     return null;
 634         final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
 635         ComponentPeer peer = acc.getPeer(comp);
 636         if (peer==null)     return null;
 637 
 638         while (peer instanceof java.awt.peer.LightweightPeer) {
 639             comp = comp.getParent();
 640             if (comp==null) return null;
 641             peer = acc.getPeer(comp);
 642             if (peer==null) return null;
 643         }
 644 
 645         if (peer instanceof WComponentPeer)
 646             return (WComponentPeer)peer;
 647         else
 648             return null;
 649 
 650     }
 651 
 652     private native int createNativeContext();
 653     private native void destroyNativeContext(int context);
 654     private native void enableNativeIME(WComponentPeer peer, int context, boolean useNativeCompWindow);
 655     private native void disableNativeIME(WComponentPeer peer);
 656     private native void handleNativeIMEEvent(WComponentPeer peer, AWTEvent e);
 657     private native void endCompositionNative(int context, boolean flag);
 658     private native void setConversionStatus(int context, int cmode);
 659     private native int  getConversionStatus(int context);
 660     private native void setOpenStatus(int context, boolean flag);
 661     private native boolean getOpenStatus(int context);
 662     private native void setStatusWindowVisible(WComponentPeer peer, boolean visible);
 663     private native String getNativeIMMDescription();
 664     static native Locale getNativeLocale();
 665     static native boolean setNativeLocale(String localeName, boolean onActivate);
 666     private native void openCandidateWindow(WComponentPeer peer, int x, int y);
 667     private native boolean isCompositionStringAvailable(int context);
 668 }