/*
* This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com>
* Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/)
*/
/*
* @(#)X11InputMethod.java 1.74 03/01/23
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package sun.awt.motif;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.HashMap;
import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Window;
import java.awt.im.InputContext;
import java.awt.im.InputMethodHighlight;
import java.awt.im.spi.InputMethodContext;
import sun.awt.im.InputMethodAdapter;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.FocusEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowEvent;
import java.awt.event.InputMethodEvent;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
import java.lang.Character.Subset;
import java.text.AttributedString;
import java.text.AttributedCharacterIterator;
import sun.awt.motif.MComponentPeer;
import sun.awt.motif.MInputMethodControl;
/**
* Input Method Adapter for XIM
*
* @version 1.74 01/23/03
* @author JavaSoft International
*/
public class X11InputMethod extends InputMethodAdapter {
/*
* The following XIM* values must be the same as those defined in
* Xlib.h
*/
private static final int XIMReverse = (1<<0);
private static final int XIMUnderline = (1<<1);
private static final int XIMHighlight = (1<<2);
private static final int XIMPrimary = (1<<5);
private static final int XIMSecondary = (1<<6);
private static final int XIMTertiary = (1<<7);
/*
* visible position values
*/
private static final int XIMVisibleToForward = (1<<8);
private static final int XIMVisibleToBackward = (1<<9);
private static final int XIMVisibleCenter = (1<<10);
private static final int XIMVisibleMask = (XIMVisibleToForward|
XIMVisibleToBackward|
XIMVisibleCenter);
private Locale locale;
private static boolean isXIMOpened = false;
private Container clientComponentWindow = null;
private boolean createXICFailed = false;
private Component awtFocussedComponent = null;
private Component xicFocussedComponent = null; // only used while input method is inactive
private boolean isActive = false;
private boolean isActiveClient = false;
private static Map[] highlightStyles;
private boolean disposed = false;
//reset the XIC if necessary
private boolean needResetXIC = false;
private Component needResetXICClient = null;
// composition state stuff
private boolean compositionEnableSupported = true;
private boolean needCompositionEnable = false;
// variables to keep track of preedit context.
private String committedText = null;
private StringBuffer composedText = null;
private IntBuffer rawFeedbacks;
// private data (X11InputMethodData structure defined in
// awt_InputMethod.c) for native methods
// this structure needs to be accessed within AWT_LOCK/UNLOCK
transient private long pData = 0; // accessed by native
// Initialize highlight mapping table
static {
Map styles[] = new Map[4];
HashMap map;
// UNSELECTED_RAW_TEXT_HIGHLIGHT
map = new HashMap(1);
map.put(TextAttribute.WEIGHT,
TextAttribute.WEIGHT_BOLD);
styles[0] = Collections.unmodifiableMap(map);
// SELECTED_RAW_TEXT_HIGHLIGHT
map = new HashMap(1);
map.put(TextAttribute.SWAP_COLORS,
TextAttribute.SWAP_COLORS_ON);
styles[1] = Collections.unmodifiableMap(map);
// UNSELECTED_CONVERTED_TEXT_HIGHLIGHT
map = new HashMap(1);
map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
styles[2] = Collections.unmodifiableMap(map);
// SELECTED_CONVERTED_TEXT_HIGHLIGHT
map = new HashMap(1);
map.put(TextAttribute.SWAP_COLORS,
TextAttribute.SWAP_COLORS_ON);
styles[3] = Collections.unmodifiableMap(map);
highlightStyles = styles;
}
static {
initIDs();
}
/**
* Initialize JNI field and method IDs for fields that may be
accessed from C.
*/
private static native void initIDs();
/**
* Constructs an X11InputMethod instance. It initializes the XIM
* environment if it's not done yet.
*
* @exception AWTException if XOpenIM() failed.
*/
public X11InputMethod() throws AWTException {
// supports only the locale in which the VM is started
locale = X11InputMethodDescriptor.getSupportedLocale();
if (initXIM() == false) {
throw new AWTException("Cannot open X Input Method");
}
}
protected void finalize() throws Throwable {
dispose();
super.finalize();
}
protected void awtLock() {
AWTLockAccess.awtLock();
}
protected void awtUnlock() {
AWTLockAccess.awtUnlock();
}
/**
* Invokes openIM() that invokes XOpenIM() if it's not opened yet.
* @return true if openXIM() is successful or it's already been opened.
*/
private synchronized boolean initXIM() {
if (isXIMOpened == false)
isXIMOpened = openXIM();
return isXIMOpened;
}
/**
* Does nothing - this adapter doesn't use the input method context.
*
* @see java.awt.im.spi.InputMethod#setInputMethodContext
*/
public void setInputMethodContext(InputMethodContext context) {
}
/**
* Set locale to input. If input method doesn't support specified locale,
* false will be returned and its behavior is not changed.
*
* @param lang locale to input
* @return the true is returned when specified locale is supported.
*/
public boolean setLocale(Locale lang) {
if (lang.equals(locale)) {
return true;
}
// special compatibility rule for Japanese and Korean
if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||
locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {
return true;
}
return false;
}
/**
* Returns current input locale.
*/
public Locale getLocale() {
return locale;
}
/**
* Does nothing - XIM doesn't let you specify which characters you expect.
*
* @see java.awt.im.spi.InputMethod#setCharacterSubsets
*/
public void setCharacterSubsets(Subset[] subsets) {
}
/**
* Dispatch event to input method. InputContext dispatch event with this
* method. Input method set consume flag if event is consumed in
* input method.
*
* @param e event
*/
public void dispatchEvent(AWTEvent e) {
}
private final void resetXICifneeded(){
if (needResetXIC){
if (getClientComponent() != needResetXICClient){
resetXIC();
// Restore the composition state if needed
if (compositionEnableSupported && needCompositionEnable) {
try {
setCompositionEnabled(true);
} catch (UnsupportedOperationException e) {
compositionEnableSupported = false;
}
}
}
needResetXICClient = null;
needResetXIC = false;
needCompositionEnable = false;
}
}
/**
* Activate input method.
*/
public synchronized void activate() {
if (createXICFailed)
return;
resetXICifneeded();
clientComponentWindow = getClientComponentWindow();
if (clientComponentWindow == null)
return;
if (xicFocussedComponent != null){
if (xicFocussedComponent != awtFocussedComponent)
setXICFocus(getPeer(xicFocussedComponent), false, isActiveClient);
xicFocussedComponent = null;
}
if (pData == 0) {
MComponentPeer peer = getPeer(clientComponentWindow);
if (peer == null) {
return;
}
MComponentPeer tc = null;
if (peer instanceof MInputMethodControl) {
tc = ((MInputMethodControl)peer).getTextComponent();
}
if (!createXICNative(peer, tc)) {
createXICFailed = true;
return;
}
disposed = false;
if (peer instanceof MInputMethodControl) {
((MInputMethodControl)peer).addInputMethod(this);
}
}
setXICFocus(getPeer(awtFocussedComponent), true, haveActiveClient());
isActive = true;
}
/**
* Deactivate input method.
*/
public synchronized void deactivate(boolean isTemporary) {
boolean isAc = haveActiveClient();
if (isTemporary){
xicFocussedComponent = awtFocussedComponent;
isActiveClient = isAc;
//turn the status window off...
turnoffStatusWindow();
} else {
setXICFocus(getPeer(awtFocussedComponent), false, isAc);
xicFocussedComponent = null;
}
isActive = false;
}
// implements java.awt.im.spi.InputMethod.hideWindows
public void hideWindows() {
// ? ? ? need real implementation
}
/**
* @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#setAWTFocussedComponent
*/
protected void setAWTFocussedComponent(Component component) {
if (component == null) {
return;
}
if (isActive) {
// deactivate/activate are being suppressed during a focus change -
// this may happen when an input method window is made visible
boolean ac = haveActiveClient();
setXICFocus(getPeer(awtFocussedComponent), false, ac);
setXICFocus(getPeer(component), true, ac);
}
awtFocussedComponent = component;
}
/**
* @see sun.awt.im.InputMethodAdapter#stopListening
*/
protected void stopListening() {
// It is desirable to disable XIM by calling XSetICValues with
// XNPreeditState == XIMPreeditDisable. But Solaris 2.6 and
// Solaris 7 do not implement this correctly without a patch,
// so just call resetXIC here. Prior endComposition call commits
// the existing composed text.
endComposition();
if (needResetXIC) {
resetXIC();
needResetXICClient = null;
needResetXIC = false;
}
}
/**
* Returns the Window instance in which the client component is
* contained. If not found, null is returned. (IS THIS POSSIBLE?)
*/
// NOTE: This method may be called by privileged threads.
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
private Window getClientComponentWindow() {
Component client = getClientComponent();
Container container;
if (client instanceof Container) {
container = (Container) client;
} else {
// SECURITY: Use _NoClientCode(), because this thread may
// be privileged
container = MComponentPeer.getParent_NoClientCode(client);
}
while (container != null && !(container instanceof java.awt.Window)) {
// SECURITY: Use _NoClientCode(), because this thread may
// be privileged
container = MComponentPeer.getParent_NoClientCode(container);
}
return (Window) container;
}
/**
* Returns peer of the given client component. If the given client component
* doesn't have peer, peer of the native container of the client is returned.
*/
private MComponentPeer getPeer(Component client) {
MComponentPeer peer = (MComponentPeer)MToolkit.targetToPeer(client);
if (peer != null)
return peer;
Container nativeContainer = MToolkit.getNativeContainer(client);
peer = (MComponentPeer)MToolkit.targetToPeer(nativeContainer);
return peer;
}
/**
* Changes the status area configuration that is to be requested
* by Frame or Dialog.
*/
void configureStatus() {
if (disposed) {
return;
}
MComponentPeer peer = getPeer((Window) clientComponentWindow);
MComponentPeer tc = ((MInputMethodControl)peer).getTextComponent();
if (tc != null) {
configureStatusAreaNative(tc);
}
}
/**
* Creates an input method event from the arguments given
* and posts it on the AWT event queue. For arguments,
* see InputMethodEvent. Called by input method.
*
* @see java.awt.event.InputMethodEvent#InputMethodEvent
*/
private void postInputMethodEvent(int id,
AttributedCharacterIterator text,
int committedCharacterCount,
TextHitInfo caret,
TextHitInfo visiblePosition,
long when) {
Component source = getClientComponent();
if (source != null) {
InputMethodEvent event = new InputMethodEvent(source,
id, when, text, committedCharacterCount, caret, visiblePosition);
MToolkit.postEvent(MToolkit.targetToAppContext(source), (AWTEvent)event);
}
}
private void postInputMethodEvent(int id,
AttributedCharacterIterator text,
int committedCharacterCount,
TextHitInfo caret,
TextHitInfo visiblePosition) {
postInputMethodEvent(id, text, committedCharacterCount,
caret, visiblePosition, EventQueue.getMostRecentEventTime());
}
/**
* Dispatches committed text from XIM to the awt event queue. This
* method is invoked from the event handler in canvas.c in the
* AWT-Motif thread context.
* @param str committed text
* @param long when
*/
// NOTE: This method may be called by privileged threads.
// This functionality is implemented in a package-private method
// to insure that it cannot be overridden by client subclasses.
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
void dispatchCommittedText(String str, long when) {
if (str == null)
return;
if (composedText == null) {
AttributedString attrstr = new AttributedString(str);
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
attrstr.getIterator(),
str.length(),
null,
null,
when);
} else {
// if there is composed text, wait until the preedit
// callback is invoked.
committedText = str;
}
}
private void dispatchCommittedText(String str) {
dispatchCommittedText(str, EventQueue.getMostRecentEventTime());
}
/**
* Updates composed text with XIM preedit information and
* posts composed text to the awt event queue. The args of
* this method correspond to the XIM preedit callback
* information. The XIM highlight attributes are translated via
* fixed mapping (i.e., independent from any underlying input
* method engine). This method is invoked in the AWT-Motif
* (event loop) thread context.
*/
// NOTE: This method may be called by privileged threads.
// This functionality is implemented in a package-private method
// to insure that it cannot be overridden by client subclasses.
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
void dispatchComposedText(String chgText,
int chgStyles[],
int chgOffset,
int chgLength,
int caretPosition,
long when) {
if (disposed) {
return;
}
//Workaround for deadlock bug on solaris2.6_zh bug#4170760
if (chgText == null
&& chgStyles == null
&& chgOffset == 0
&& chgLength == 0
&& caretPosition == 0
&& composedText == null
&& committedText == null)
return;
/* synchronized (this) */ {
if (composedText == null) {
// TODO: avoid reallocation of those buffers
composedText = new StringBuffer(INITIAL_SIZE);
rawFeedbacks = new IntBuffer(INITIAL_SIZE);
}
if (chgLength > 0) {
if (chgText == null && chgStyles != null) {
rawFeedbacks.replace(chgOffset, chgStyles);
} else {
if (chgLength == composedText.length()) {
// optimization for the special case to replace the
// entire previous text
composedText = new StringBuffer(INITIAL_SIZE);
rawFeedbacks = new IntBuffer(INITIAL_SIZE);
} else {
if (composedText.length() > 0) {
if (chgOffset+chgLength < composedText.length()) {
String text;
text = composedText.toString().substring(chgOffset+chgLength,
composedText.length());
composedText.setLength(chgOffset);
composedText.append(text);
} else {
// in case to remove substring from chgOffset
// to the end
composedText.setLength(chgOffset);
}
rawFeedbacks.remove(chgOffset, chgLength);
}
}
}
}
if (chgText != null) {
composedText.insert(chgOffset, chgText);
if (chgStyles != null)
rawFeedbacks.insert(chgOffset, chgStyles);
}
if (composedText.length() == 0) {
composedText = null;
rawFeedbacks = null;
// if there is any outstanding committed text stored by
// dispatchCommittedText(), it has to be sent to the
// client component.
if (committedText != null) {
dispatchCommittedText(committedText, when);
committedText = null;
return;
}
// otherwise, send null text to delete client's composed
// text.
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
null,
0,
null,
null,
when);
return;
}
// Now sending the composed text to the client
int composedOffset;
AttributedString inputText;
// if there is any partially committed text, concatenate it to
// the composed text.
if (committedText != null) {
composedOffset = committedText.length();
inputText = new AttributedString(committedText + composedText);
committedText = null;
} else {
composedOffset = 0;
inputText = new AttributedString(composedText.toString());
}
int currentFeedback;
int nextFeedback;
int startOffset = 0;
int currentOffset;
int visiblePosition = 0;
TextHitInfo visiblePositionInfo = null;
rawFeedbacks.rewind();
currentFeedback = rawFeedbacks.getNext();
rawFeedbacks.unget();
while ((nextFeedback = rawFeedbacks.getNext()) != -1) {
if (visiblePosition == 0) {
visiblePosition = nextFeedback & XIMVisibleMask;
if (visiblePosition != 0) {
int index = rawFeedbacks.getOffset() - 1;
if (visiblePosition == XIMVisibleToBackward)
visiblePositionInfo = TextHitInfo.leading(index);
else
visiblePositionInfo = TextHitInfo.trailing(index);
}
}
nextFeedback &= ~XIMVisibleMask;
if (currentFeedback != nextFeedback) {
rawFeedbacks.unget();
currentOffset = rawFeedbacks.getOffset();
inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
convertVisualFeedbackToHighlight(currentFeedback),
composedOffset + startOffset,
composedOffset + currentOffset);
startOffset = currentOffset;
currentFeedback = nextFeedback;
}
}
currentOffset = rawFeedbacks.getOffset();
if (currentOffset >= 0) {
inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
convertVisualFeedbackToHighlight(currentFeedback),
composedOffset + startOffset,
composedOffset + currentOffset);
}
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
inputText.getIterator(),
composedOffset,
TextHitInfo.leading(caretPosition),
visiblePositionInfo,
when);
}
}
/*
* Subclasses should override disposeImpl() instead of dispose(). Client
* code should always invoke dispose(), never disposeImpl().
*/
protected synchronized void disposeImpl() {
if (clientComponentWindow != null) {
MComponentPeer peer = getPeer(clientComponentWindow);
if (peer instanceof MInputMethodControl)
((MInputMethodControl)peer).removeInputMethod(this);
clientComponentWindow = null;
}
disposeXIC();
awtLock();
composedText = null;
committedText = null;
rawFeedbacks = null;
awtUnlock();
awtFocussedComponent = null;
xicFocussedComponent = null;
}
/**
* Frees all X Window resources associated with this object.
*
* @see java.awt.im.spi.InputMethod#dispose
*/
public final void dispose() {
boolean call_disposeImpl = false;
if (!disposed) {
synchronized (this) {
if (!disposed) {
disposed = call_disposeImpl = true;
}
}
}
if (call_disposeImpl) {
disposeImpl();
}
}
/**
* Returns null.
*
* @see java.awt.im.spi.InputMethod#getControlObject
*/
public Object getControlObject() {
return null;
}
/**
* @see java.awt.im.spi.InputMethod#removeNotify
*/
public synchronized void removeNotify() {
if (MToolkit.targetToPeer(getClientComponent()) != null) {
dispose();
} else {
// We do not have to dispose XICs in case of lightweight component.
resetXIC();
}
}
/**
* @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
*/
public void setCompositionEnabled(boolean enable) {
setCompositionEnabledNative(enable);
}
/**
* @see java.awt.im.spi.InputMethod#isCompositionEnabled
*/
public boolean isCompositionEnabled() {
return isCompositionEnabledNative();
}
/**
* Ends any input composition that may currently be going on in this
* context. Depending on the platform and possibly user preferences,
* this may commit or delete uncommitted text. Any changes to the text
* are communicated to the active component using an input method event.
*
* <p>
* A text editing component may call this in a variety of situations,
* for example, when the user moves the insertion point within the text
* (but outside the composed text), or when the component's text is
* saved to a file or copied to the clipboard.
*
*/
public void endComposition() {
if (disposed) {
return;
}
// Remember composition state before calling resetXIC, if possible.
boolean isCompositionEnabled = false;
if (compositionEnableSupported) {
try {
isCompositionEnabled = isCompositionEnabled();
} catch (UnsupportedOperationException e) {
compositionEnableSupported = false;
}
}
if (haveActiveClient()
&& composedText == null
&& committedText == null){
needResetXIC = true;
needResetXICClient = getClientComponent();
needCompositionEnable = isCompositionEnabled;
return;
}
String text = resetXIC();
needResetXIC = false;
// Remove any existing composed text by posting an InputMethodEvent
// with null composed text. It would be desirable to wait for a
// dispatchComposedText call from X input method engine, but some
// input method does not conform to the XIM specification and does
// not call the preedit callback to erase preedit text on calling
// XmbResetIC. To work around this problem, do it here by ourselves.
awtLock();
try {
composedText = null;
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
null,
0,
null,
null);
if (text != null && text.length() > 0) {
dispatchCommittedText(text);
}
}
finally {
awtUnlock();
}
// Restore the preedit state if it was enabled
if (compositionEnableSupported && isCompositionEnabled) {
try {
setCompositionEnabled(true);
} catch (UnsupportedOperationException e) {
compositionEnableSupported = false;
}
}
}
/**
* Changes the internal XIC configurations. This is required the
* case that addition or elimination of text components has
* happened in the containment hierarchy. This method is invoked
* by Frame or Dialog.
*/
synchronized void reconfigureXIC(MInputMethodControl control) {
if (!disposed) {
// Some IM servers require to reset XIC before destroying
// the XIC. I.e., Destroying XIC doesn't reset the internal
// state of the IM server. endComposition() takes care of
// resetting XIC and preedit synchronization. However,
// there is no client at this point. It is assumed that
// the previous client is still available for dispatching
// committed text which maintains client's composition
// context.
endComposition();
resetXICifneeded();
reconfigureXICNative((MComponentPeer) control, control.getTextComponent());
}
}
/**
* Performs mapping from an XIM visible feedback value to Java IM highlight.
* @return Java input method highlight
*/
private InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) {
InputMethodHighlight highlight;
switch (feedback) {
case XIMUnderline:
highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
break;
case XIMReverse:
highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
break;
case XIMHighlight:
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
break;
case XIMPrimary:
highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
break;
case XIMSecondary:
highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
break;
case XIMTertiary:
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
break;
default:
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
break;
}
return highlight;
}
// initial capacity size for string buffer, etc.
private static final int INITIAL_SIZE = 64;
/**
* IntBuffer is an inner class that manipulates an int array and
* provides UNIX file io stream-like programming interfaces to
* access it. (An alternative would be to use ArrayList which may
* be too expensive for the work.)
*/
private final class IntBuffer {
private int[] intArray;
private int size;
private int index;
IntBuffer(int initialCapacity) {
intArray = new int[initialCapacity];
size = 0;
index = 0;
}
void insert(int offset, int[] values) {
int newSize = size + values.length;
if (intArray.length < newSize) {
int[] newIntArray = new int[newSize * 2];
System.arraycopy(intArray, 0, newIntArray, 0, size);
intArray = newIntArray;
}
System.arraycopy(intArray, offset, intArray, offset+values.length,
size - offset);
System.arraycopy(values, 0, intArray, offset, values.length);
size += values.length;
if (index > offset)
index = offset;
}
void remove(int offset, int length) {
if (offset + length != size)
System.arraycopy(intArray, offset+length, intArray, offset,
size - offset - length);
size -= length;
if (index > offset)
index = offset;
}
void replace(int offset, int[] values) {
System.arraycopy(values, 0, intArray, offset, values.length);
}
void removeAll() {
size = 0;
index = 0;
}
void rewind() {
index = 0;
}
int getNext() {
if (index == size)
return -1;
return intArray[index++];
}
void unget() {
if (index != 0)
index--;
}
int getOffset() {
return index;
}
public String toString() {
StringBuffer s = new StringBuffer();
for (int i = 0; i < size;) {
s.append(intArray[i++]);
if (i < size)
s.append(",");
}
return s.toString();
}
}
/*
* Native methods
*/
private native boolean openXIM();
private native boolean createXICNative(MComponentPeer peer, MComponentPeer tc);
private native void reconfigureXICNative(MComponentPeer peer,
MComponentPeer tc);
private native void setXICFocus(MComponentPeer peer,
boolean value, boolean active);
private native String resetXIC();
private native void disposeXIC();
private native void closeXIM();
private native void configureStatusAreaNative(MComponentPeer tc);
private native void setCompositionEnabledNative(boolean enable);
private native boolean isCompositionEnabledNative();
private native void turnoffStatusWindow();
}