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