/* * This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com> * Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/) */ /* * @(#)WInputMethod.java 1.53 03/01/23 * * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package sun.awt.windows; import java.awt.*; import java.awt.peer.*; import java.awt.event.*; import java.awt.im.*; import java.awt.im.spi.InputMethod; import java.awt.im.spi.InputMethodContext; import java.awt.font.*; import java.text.*; import java.text.AttributedCharacterIterator.Attribute; import java.lang.Character.Subset; import java.lang.Character.UnicodeBlock; import java.lang.System; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import sun.awt.im.InputMethodAdapter; public class WInputMethod extends InputMethodAdapter { /** * The input method context, which is used to dispatch input method * events to the client component and to request information from * the client component. */ private InputMethodContext inputContext; private Component awtFocussedComponent; private WComponentPeer awtFocussedComponentPeer; private WComponentPeer lastFocussedComponentPeer; private boolean isLastFocussedActiveClient; private boolean isActive; private int context; private boolean open; //default open status; private int cmode; //default conversion mode; private Locale currentLocale; // attribute definition in Win32 (in IMM.H) public final static byte ATTR_INPUT = 0x00; public final static byte ATTR_TARGET_CONVERTED = 0x01; public final static byte ATTR_CONVERTED = 0x02; public final static byte ATTR_TARGET_NOTCONVERTED = 0x03; public final static byte ATTR_INPUT_ERROR = 0x04; // cmode definition in Win32 (in IMM.H) public final static int IME_CMODE_ALPHANUMERIC = 0x0000; public final static int IME_CMODE_NATIVE = 0x0001; public final static int IME_CMODE_KATAKANA = 0x0002; public final static int IME_CMODE_LANGUAGE = 0x0003; public final static int IME_CMODE_FULLSHAPE = 0x0008; public final static int IME_CMODE_HANJACONVERT = 0x0040; public final static int IME_CMODE_ROMAN = 0x0010; // flag values for endCompositionNative() behavior private final static boolean COMMIT_INPUT = true; private final static boolean DISCARD_INPUT = false; private static Map[] highlightStyles; // Initialize highlight mapping table static { Map styles[] = new Map[4]; HashMap map; // UNSELECTED_RAW_TEXT_HIGHLIGHT map = new HashMap(1); map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_DOTTED); styles[0] = Collections.unmodifiableMap(map); // SELECTED_RAW_TEXT_HIGHLIGHT map = new HashMap(1); map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_GRAY); styles[1] = Collections.unmodifiableMap(map); // UNSELECTED_CONVERTED_TEXT_HIGHLIGHT map = new HashMap(1); map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_DOTTED); styles[2] = Collections.unmodifiableMap(map); // SELECTED_CONVERTED_TEXT_HIGHLIGHT map = new HashMap(4); Color navyBlue = new Color(0, 0, 128); map.put(TextAttribute.FOREGROUND, navyBlue); map.put(TextAttribute.BACKGROUND, Color.white); map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON); map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL); styles[3] = Collections.unmodifiableMap(map); highlightStyles = styles; } public WInputMethod() { context = createNativeContext(); cmode = getConversionStatus(context); open = getOpenStatus(context); currentLocale = getNativeLocale(); if (currentLocale == null) { currentLocale = Locale.getDefault(); } } protected void finalize() throws Throwable { // Release the resources used by the native input context. if (context!=0) { destroyNativeContext(context); context=0; } super.finalize(); } public synchronized void setInputMethodContext(InputMethodContext context) { inputContext = context; } public final void dispose() { // Due to a memory management problem in Windows 98, we should retain // the native input context until this object is finalized. So do // nothing here. } /** * Returns null. * * @see java.awt.im.spi.InputMethod#getControlObject */ public Object getControlObject() { return null; } public boolean setLocale(Locale lang) { return setLocale(lang, false); } private boolean setLocale(Locale lang, boolean onActivate) { Locale[] available = WInputMethodDescriptor.getAvailableLocalesInternal(); for (int i = 0; i < available.length; i++) { Locale locale = available[i]; if (lang.equals(locale) || // special compatibility rule for Japanese and Korean locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) || locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) { if (isActive) { setNativeLocale(locale.toString(), onActivate); } currentLocale = locale; return true; } } return false; } public Locale getLocale() { if (isActive) { currentLocale = getNativeLocale(); if (currentLocale == null) { currentLocale = Locale.getDefault(); } } return currentLocale; } /** * Implements InputMethod.setCharacterSubsets for Windows. * * @see java.awt.im.spi.InputMethod#setCharacterSubsets */ public void setCharacterSubsets(Subset[] subsets) { if (subsets == null){ setConversionStatus(context, cmode); setOpenStatus(context, open); return; } // Use first subset only. Other subsets in array is ignored. // This is restriction of Win32 implementation. Subset subset1 = subsets[0]; Locale locale = getNativeLocale(); int newmode; if (locale == null) { return; } if (locale.getLanguage().equals(Locale.JAPANESE.getLanguage())) { if (subset1 == UnicodeBlock.BASIC_LATIN || subset1 == InputSubset.LATIN_DIGITS) { setOpenStatus(context, false); } else { if (subset1 == UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || subset1 == InputSubset.KANJI || subset1 == UnicodeBlock.HIRAGANA) newmode = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE; else if (subset1 == UnicodeBlock.KATAKANA) newmode = IME_CMODE_NATIVE | IME_CMODE_KATAKANA| IME_CMODE_FULLSHAPE; else if (subset1 == InputSubset.HALFWIDTH_KATAKANA) newmode = IME_CMODE_NATIVE | IME_CMODE_KATAKANA; else if (subset1 == InputSubset.FULLWIDTH_LATIN) newmode = IME_CMODE_FULLSHAPE; else return; setOpenStatus(context, true); newmode |= (getConversionStatus(context)&IME_CMODE_ROMAN); // reserve ROMAN input mode setConversionStatus(context, newmode); } } else if (locale.getLanguage().equals(Locale.KOREAN.getLanguage())) { if (subset1 == UnicodeBlock.BASIC_LATIN || subset1 == InputSubset.LATIN_DIGITS) { setOpenStatus(context, false); } else { if (subset1 == UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || subset1 == InputSubset.HANJA || subset1 == UnicodeBlock.HANGUL_SYLLABLES || subset1 == UnicodeBlock.HANGUL_JAMO || subset1 == UnicodeBlock.HANGUL_COMPATIBILITY_JAMO) newmode = IME_CMODE_NATIVE; else if (subset1 == InputSubset.FULLWIDTH_LATIN) newmode = IME_CMODE_FULLSHAPE; else return; setOpenStatus(context, true); setConversionStatus(context, newmode); } } else if (locale.getLanguage().equals(Locale.CHINESE.getLanguage())) { if (subset1 == UnicodeBlock.BASIC_LATIN || subset1 == InputSubset.LATIN_DIGITS) { setOpenStatus(context, false); } else { if (subset1 == UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || subset1 == InputSubset.TRADITIONAL_HANZI || subset1 == InputSubset.SIMPLIFIED_HANZI) newmode = IME_CMODE_NATIVE; else if (subset1 == InputSubset.FULLWIDTH_LATIN) newmode = IME_CMODE_FULLSHAPE; else return; setOpenStatus(context, true); setConversionStatus(context, newmode); } } } public void dispatchEvent(AWTEvent e) { if (e instanceof ComponentEvent) { Component comp = ((ComponentEvent) e).getComponent(); if (comp == awtFocussedComponent) { if (awtFocussedComponentPeer.isDisposed()) { awtFocussedComponentPeer = getNearestNativePeer(comp); } if (awtFocussedComponentPeer != null) { handleNativeIMEEvent(awtFocussedComponentPeer, e); } } } } public void activate() { boolean haveActive = haveActiveClient(); if (lastFocussedComponentPeer != awtFocussedComponentPeer || isLastFocussedActiveClient != haveActive) { if (lastFocussedComponentPeer != null) disableNativeIME(lastFocussedComponentPeer); if (awtFocussedComponentPeer != null) enableNativeIME(awtFocussedComponentPeer, context, !haveActive); lastFocussedComponentPeer = awtFocussedComponentPeer; isLastFocussedActiveClient = haveActive; } isActive = true; if (currentLocale != null) { setLocale(currentLocale, true); } } public void deactivate(boolean isTemporary) { // Sync currentLocale with the Windows keyboard layout which might be changed // by hot key getLocale(); if (awtFocussedComponentPeer != null) { lastFocussedComponentPeer = awtFocussedComponentPeer; isLastFocussedActiveClient = haveActiveClient(); } isActive = false; } // implements sun.awt.im.InputMethodAdapter.setAWTFocussedComponent protected void setAWTFocussedComponent(Component component) { if (component == null) { return; } WComponentPeer peer = getNearestNativePeer(component); if (isActive) { // deactivate/activate are being suppressed during a focus change - // this may happen when an input method window is made visible if (awtFocussedComponentPeer != null) { disableNativeIME(awtFocussedComponentPeer); } if (peer != null) { enableNativeIME(peer, context, !haveActiveClient()); } } awtFocussedComponent = component; awtFocussedComponentPeer = peer; } // implements java.awt.im.spi.InputMethod.hideWindows public void hideWindows() { if (awtFocussedComponentPeer != null) { hideWindowsNative(awtFocussedComponentPeer); } } /** * @see java.awt.im.spi.InputMethod#removeNotify */ public void removeNotify() { endCompositionNative(context, DISCARD_INPUT); awtFocussedComponent = null; awtFocussedComponentPeer = null; } /** * @see java.awt.Toolkit#mapInputMethodHighlight */ static Map mapInputMethodHighlight(InputMethodHighlight highlight) { int index; int state = highlight.getState(); if (state == InputMethodHighlight.RAW_TEXT) { index = 0; } else if (state == InputMethodHighlight.CONVERTED_TEXT) { index = 2; } else { return null; } if (highlight.isSelected()) { index += 1; } return highlightStyles[index]; } // see sun.awt.im.InputMethodAdapter.supportsBelowTheSpot protected boolean supportsBelowTheSpot() { return true; } public void endComposition() { //right now the native endCompositionNative() just cancel //the composition string, maybe a commtting is desired endCompositionNative(context, (haveActiveClient() ? COMMIT_INPUT : DISCARD_INPUT)); } /** * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean) */ public void setCompositionEnabled(boolean enable) { setOpenStatus(context, enable); } /** * @see java.awt.im.spi.InputMethod#isCompositionEnabled */ public boolean isCompositionEnabled() { return getOpenStatus(context); } public void sendInputMethodEvent(int id, long when, String text, int[] clauseBoundary, String[] clauseReading, int[] attributeBoundary, byte[] attributeValue, int commitedTextLength, int caretPos, int visiblePos) { AttributedCharacterIterator iterator = null; if (text!=null) { // construct AttributedString AttributedString attrStr = new AttributedString(text); // set Language Information attrStr.addAttribute(Attribute.LANGUAGE, Locale.getDefault(), 0, text.length()); // set Clause and Reading Information if (clauseBoundary!=null && clauseReading!=null && clauseReading.length!=0 && clauseBoundary.length==clauseReading.length+1 && clauseBoundary[0]==0 && clauseBoundary[clauseReading.length]==text.length() ) { for (int i=0; i<clauseBoundary.length-1; i++) { attrStr.addAttribute(Attribute.INPUT_METHOD_SEGMENT, new Annotation(null), clauseBoundary[i], clauseBoundary[i+1]); attrStr.addAttribute(Attribute.READING, new Annotation(clauseReading[i]), clauseBoundary[i], clauseBoundary[i+1]); } } else { // if (clauseBoundary != null) // System.out.println("Invalid clause information!"); attrStr.addAttribute(Attribute.INPUT_METHOD_SEGMENT, new Annotation(null), 0, text.length()); attrStr.addAttribute(Attribute.READING, new Annotation(new String("")), 0, text.length()); } // set Hilight Information if (attributeBoundary!=null && attributeValue!=null && attributeValue.length!=0 && attributeBoundary.length==attributeValue.length+1 && attributeBoundary[0]==0 && attributeBoundary[attributeValue.length]==text.length() ) { for (int i=0; i<attributeBoundary.length-1; i++) { InputMethodHighlight highlight; switch (attributeValue[i]) { case ATTR_INPUT: case ATTR_INPUT_ERROR: default: highlight = InputMethodHighlight.UNSELECTED_RAW_TEXT_HIGHLIGHT; break; case ATTR_TARGET_CONVERTED: highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT; break; case ATTR_CONVERTED: highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT; break; case ATTR_TARGET_NOTCONVERTED: highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; break; } attrStr.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, highlight, attributeBoundary[i], attributeBoundary[i+1]); } } else { // if (attributeBoundary != null) // System.out.println("Invalid attribute information!"); attrStr.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT, 0, text.length()); } // get iterator iterator = attrStr.getIterator(); } Component source = getClientComponent(); if (source == null) return; InputMethodEvent event = new InputMethodEvent(source, id, when, iterator, commitedTextLength, TextHitInfo.leading(caretPos), TextHitInfo.leading(visiblePos)); WToolkit.postEvent(WToolkit.targetToAppContext(source), event); } public void inquireCandidatePosition() { // This call should return immediately just to cause // InputMethodRequests.getTextLocation be called within // AWT Event thread. Otherwise, a potential deadlock // could happen. java.awt.EventQueue.invokeLater(new Runnable() { public void run() { int x = 0; int y = 0; Component client = getClientComponent(); if (client != null) { if (haveActiveClient()) { Rectangle rc = inputContext.getTextLocation(TextHitInfo.leading(0)); x = rc.x; y = rc.y + rc.height; } else { Point pt = client.getLocationOnScreen(); Dimension size = client.getSize(); x = pt.x; y = pt.y + size.height; } } openCandidateWindow(awtFocussedComponentPeer, x, y); } }); } // java.awt.Toolkit#getNativeContainer() is not available // from this package private WComponentPeer getNearestNativePeer(Component comp) { if (comp==null) return null; ComponentPeer peer = comp.getPeer(); if (peer==null) return null; while (peer instanceof java.awt.peer.LightweightPeer) { comp = comp.getParent(); if (comp==null) return null; peer = comp.getPeer(); if (peer==null) return null; } if (peer instanceof WComponentPeer) return (WComponentPeer)peer; else return null; } private native int createNativeContext(); private native void destroyNativeContext(int context); private native void enableNativeIME(WComponentPeer peer, int context, boolean useNativeCompWindow); private native void disableNativeIME(WComponentPeer peer); private native void handleNativeIMEEvent(WComponentPeer peer, AWTEvent e); private native void endCompositionNative(int context, boolean flag); private native void setConversionStatus(int context, int cmode); private native int getConversionStatus(int context); private native void setOpenStatus(int context, boolean flag); private native boolean getOpenStatus(int context); static native Locale getNativeLocale(); static native boolean setNativeLocale(String localeName, boolean onActivate); private native void hideWindowsNative(WComponentPeer awtFocussedComponentPeer); private native void openCandidateWindow(WComponentPeer peer, int x, int y); }