mirror of
https://github.com/2003scape/deep-c-rsc.git
synced 2024-03-22 05:49:51 -04:00
543 lines
20 KiB
Java
543 lines
20 KiB
Java
/*
|
|
* 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);
|
|
}
|