Path: blob/jdk8u272-b10-aarch32-20201026/jdk/src/macosx/classes/sun/lwawt/macosx/CInputMethod.java
48795 views
/*1* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.lwawt.macosx;2627import java.awt.im.spi.*;28import java.util.*;29import java.awt.*;30import java.awt.peer.*;31import java.awt.event.*;32import java.awt.im.*;33import java.awt.font.*;34import java.lang.Character.Subset;35import java.lang.reflect.InvocationTargetException;36import java.text.AttributedCharacterIterator.Attribute;37import java.text.*;38import javax.swing.text.JTextComponent;3940import sun.awt.im.InputMethodAdapter;41import sun.lwawt.*;4243public class CInputMethod extends InputMethodAdapter {44private InputMethodContext fIMContext;45private Component fAwtFocussedComponent;46private LWComponentPeer fAwtFocussedComponentPeer;47private boolean isActive;4849private static Map<TextAttribute, Integer>[] sHighlightStyles;5051// Intitalize highlight mapping table and its mapper.52static {53Map<TextAttribute, Integer> styles[] = new Map[4];54HashMap<TextAttribute, Integer> map;5556// UNSELECTED_RAW_TEXT_HIGHLIGHT57map = new HashMap<TextAttribute, Integer>(1);58map.put(TextAttribute.INPUT_METHOD_UNDERLINE,59TextAttribute.UNDERLINE_LOW_GRAY);60styles[0] = Collections.unmodifiableMap(map);6162// SELECTED_RAW_TEXT_HIGHLIGHT63map = new HashMap<TextAttribute, Integer>(1);64map.put(TextAttribute.INPUT_METHOD_UNDERLINE,65TextAttribute.UNDERLINE_LOW_GRAY);66styles[1] = Collections.unmodifiableMap(map);6768// UNSELECTED_CONVERTED_TEXT_HIGHLIGHT69map = new HashMap<TextAttribute, Integer>(1);70map.put(TextAttribute.INPUT_METHOD_UNDERLINE,71TextAttribute.UNDERLINE_LOW_ONE_PIXEL);72styles[2] = Collections.unmodifiableMap(map);7374// SELECTED_CONVERTED_TEXT_HIGHLIGHT75map = new HashMap<TextAttribute, Integer>(1);76map.put(TextAttribute.INPUT_METHOD_UNDERLINE,77TextAttribute.UNDERLINE_LOW_TWO_PIXEL);78styles[3] = Collections.unmodifiableMap(map);7980sHighlightStyles = styles;8182nativeInit();8384}8586public CInputMethod() {87}888990/**91* Sets the input method context, which is used to dispatch input method92* events to the client component and to request information from93* the client component.94* <p>95* This method is called once immediately after instantiating this input96* method.97*98* @param context the input method context for this input method99* @exception NullPointerException if <code>context</code> is null100*/101public void setInputMethodContext(InputMethodContext context) {102fIMContext = context;103}104105/**106* Attempts to set the input locale. If the input method supports the107* desired locale, it changes its behavior to support input for the locale108* and returns true.109* Otherwise, it returns false and does not change its behavior.110* <p>111* This method is called112* <ul>113* <li>by {@link java.awt.im.InputContext#selectInputMethod InputContext.selectInputMethod},114* <li>when switching to this input method through the user interface if the user115* specified a locale or if the previously selected input method's116* {@link java.awt.im.spi.InputMethod#getLocale getLocale} method117* returns a non-null value.118* </ul>119*120* @param lang locale to input121* @return whether the specified locale is supported122* @exception NullPointerException if <code>locale</code> is null123*/124public boolean setLocale(Locale lang) {125return setLocale(lang, false);126}127128private boolean setLocale(Locale lang, boolean onActivate) {129Object[] available = CInputMethodDescriptor.getAvailableLocalesInternal();130for (int i = 0; i < available.length; i++) {131Locale locale = (Locale)available[i];132if (lang.equals(locale) ||133// special compatibility rule for Japanese and Korean134locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||135locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {136if (isActive) {137setNativeLocale(locale.toString(), onActivate);138}139return true;140}141}142return false;143}144145/**146* Returns the current input locale. Might return null in exceptional cases.147* <p>148* This method is called149* <ul>150* <li>by {@link java.awt.im.InputContext#getLocale InputContext.getLocale} and151* <li>when switching from this input method to a different one through the152* user interface.153* </ul>154*155* @return the current input locale, or null156*/157public Locale getLocale() {158// On Mac OS X we'll ask the currently active input method what its locale is.159Locale returnValue = getNativeLocale();160if (returnValue == null) {161returnValue = Locale.getDefault();162}163164return returnValue;165}166167/**168* Sets the subsets of the Unicode character set that this input method169* is allowed to input. Null may be passed in to indicate that all170* characters are allowed.171* <p>172* This method is called173* <ul>174* <li>immediately after instantiating this input method,175* <li>when switching to this input method from a different one, and176* <li>by {@link java.awt.im.InputContext#setCharacterSubsets InputContext.setCharacterSubsets}.177* </ul>178*179* @param subsets the subsets of the Unicode character set from which180* characters may be input181*/182public void setCharacterSubsets(Subset[] subsets) {183// -- SAK: Does mac OS X support this?184}185186/**187* Composition cannot be set on Mac OS X -- the input method remembers this188*/189public void setCompositionEnabled(boolean enable) {190throw new UnsupportedOperationException("Can't adjust composition mode on Mac OS X.");191}192193public boolean isCompositionEnabled() {194throw new UnsupportedOperationException("Can't adjust composition mode on Mac OS X.");195}196197/**198* Dispatches the event to the input method. If input method support is199* enabled for the focussed component, incoming events of certain types200* are dispatched to the current input method for this component before201* they are dispatched to the component's methods or event listeners.202* The input method decides whether it needs to handle the event. If it203* does, it also calls the event's <code>consume</code> method; this204* causes the event to not get dispatched to the component's event205* processing methods or event listeners.206* <p>207* Events are dispatched if they are instances of InputEvent or its208* subclasses.209* This includes instances of the AWT classes KeyEvent and MouseEvent.210* <p>211* This method is called by {@link java.awt.im.InputContext#dispatchEvent InputContext.dispatchEvent}.212*213* @param event the event being dispatched to the input method214* @exception NullPointerException if <code>event</code> is null215*/216public void dispatchEvent(final AWTEvent event) {217// No-op for Mac OS X.218}219220221/**222* Activate and deactivate are no-ops on Mac OS X.223* A non-US keyboard layout is an 'input method' in that it generates events the same way as224* a CJK input method. A component that doesn't want input method events still wants the dead-key225* events.226*227*228*/229public void activate() {230isActive = true;231}232233public void deactivate(boolean isTemporary) {234isActive = false;235}236237/**238* Closes or hides all windows opened by this input method instance or239* its class. Deactivate hides windows for us on Mac OS X.240*/241public void hideWindows() {242}243244long getNativeViewPtr(LWComponentPeer peer) {245if (peer.getPlatformWindow() instanceof CPlatformWindow) {246CPlatformWindow platformWindow = (CPlatformWindow) peer.getPlatformWindow();247CPlatformView platformView = platformWindow.getContentView();248return platformView.getAWTView();249} else {250return 0;251}252}253254/**255* Notifies the input method that a client component has been256* removed from its containment hierarchy, or that input method257* support has been disabled for the component.258*/259public void removeNotify() {260if (fAwtFocussedComponentPeer != null) {261nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));262}263264fAwtFocussedComponentPeer = null;265}266267/**268* Informs the input method adapter about the component that has the AWT269* focus if it's using the input context owning this adapter instance.270* We also take the opportunity to tell the native side that we are the input method271* to talk to when responding to key events.272*/273protected void setAWTFocussedComponent(Component component) {274LWComponentPeer peer = null;275long modelPtr = 0;276CInputMethod imInstance = this;277278// component will be null when we are told there's no focused component.279// When that happens we need to notify the native architecture to stop generating IMEs280if (component == null) {281peer = fAwtFocussedComponentPeer;282imInstance = null;283} else {284peer = getNearestNativePeer(component);285286// If we have a passive client, don't pass input method events to it.287if (component.getInputMethodRequests() == null) {288imInstance = null;289}290}291292if (peer != null) {293modelPtr = getNativeViewPtr(peer);294295// modelPtr refers to the ControlModel that either got or lost focus.296nativeNotifyPeer(modelPtr, imInstance);297}298299// Track the focused component and its nearest peer.300fAwtFocussedComponent = component;301fAwtFocussedComponentPeer = getNearestNativePeer(component);302}303304/**305* @see java.awt.Toolkit#mapInputMethodHighlight306*/307public static Map mapInputMethodHighlight(InputMethodHighlight highlight) {308int index;309int state = highlight.getState();310if (state == InputMethodHighlight.RAW_TEXT) {311index = 0;312} else if (state == InputMethodHighlight.CONVERTED_TEXT) {313index = 2;314} else {315return null;316}317if (highlight.isSelected()) {318index += 1;319}320return sHighlightStyles[index];321}322323/**324* Ends any input composition that may currently be going on in this325* context. Depending on the platform and possibly user preferences,326* this may commit or delete uncommitted text. Any changes to the text327* are communicated to the active component using an input method event.328*329* <p>330* A text editing component may call this in a variety of situations,331* for example, when the user moves the insertion point within the text332* (but outside the composed text), or when the component's text is333* saved to a file or copied to the clipboard.334* <p>335* This method is called336* <ul>337* <li>by {@link java.awt.im.InputContext#endComposition InputContext.endComposition},338* <li>by {@link java.awt.im.InputContext#dispatchEvent InputContext.dispatchEvent}339* when switching to a different client component340* <li>when switching from this input method to a different one using the341* user interface or342* {@link java.awt.im.InputContext#selectInputMethod InputContext.selectInputMethod}.343* </ul>344*/345public void endComposition() {346if (fAwtFocussedComponentPeer != null)347nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));348}349350/**351* Disposes of the input method and releases the resources used by it.352* In particular, the input method should dispose windows and close files that are no353* longer needed.354* <p>355* This method is called by {@link java.awt.im.InputContext#dispose InputContext.dispose}.356* <p>357* The method is only called when the input method is inactive.358* No method of this interface is called on this instance after dispose.359*/360public void dispose() {361fIMContext = null;362fAwtFocussedComponent = null;363fAwtFocussedComponentPeer = null;364}365366/**367* Returns a control object from this input method, or null. A368* control object provides methods that control the behavior of the369* input method or obtain information from the input method. The type370* of the object is an input method specific class. Clients have to371* compare the result against known input method control object372* classes and cast to the appropriate class to invoke the methods373* provided.374* <p>375* This method is called by376* {@link java.awt.im.InputContext#getInputMethodControlObject InputContext.getInputMethodControlObject}.377*378* @return a control object from this input method, or null379*/380public Object getControlObject() {381return null;382}383384// java.awt.Toolkit#getNativeContainer() is not available385// from this package386private LWComponentPeer getNearestNativePeer(Component comp) {387if (comp==null)388return null;389390ComponentPeer peer = comp.getPeer();391if (peer==null)392return null;393394while (peer instanceof java.awt.peer.LightweightPeer) {395comp = comp.getParent();396if (comp==null)397return null;398peer = comp.getPeer();399if (peer==null)400return null;401}402403if (peer instanceof LWComponentPeer)404return (LWComponentPeer)peer;405406return null;407}408409// =========================== NSTextInput callbacks ===========================410// The 'marked text' that we get from Cocoa. We need to track this separately, since411// Java doesn't let us ask the IM context for it.412private AttributedString fCurrentText = null;413private String fCurrentTextAsString = null;414private int fCurrentTextLength = 0;415416/**417* Tell the component to commit all of the characters in the string to the current418* text view. This effectively wipes out any text in progress.419*/420synchronized private void insertText(String aString) {421AttributedString attribString = new AttributedString(aString);422423// Set locale information on the new string.424attribString.addAttribute(Attribute.LANGUAGE, getLocale(), 0, aString.length());425426TextHitInfo theCaret = TextHitInfo.afterOffset(aString.length() - 1);427InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,428InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,429attribString.getIterator(),430aString.length(),431theCaret,432theCaret);433LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);434fCurrentText = null;435fCurrentTextAsString = null;436fCurrentTextLength = 0;437}438439private void startIMUpdate (String rawText) {440fCurrentTextAsString = new String(rawText);441fCurrentText = new AttributedString(fCurrentTextAsString);442fCurrentTextLength = rawText.length();443}444445static private final int kCaretPosition = 0;446static private final int kRawText = 1;447static private final int kSelectedRawText = 2;448static private final int kConvertedText = 3;449static private final int kSelectedConvertedText = 4;450451/**452* Convert Cocoa text highlight attributes into Java input method highlighting.453*/454private void addAttribute (boolean isThickUnderline, boolean isGray, int start, int length) {455int begin = start;456int end = start + length;457int markupType = kRawText;458459if (isThickUnderline && isGray) {460markupType = kRawText;461} else if (!isThickUnderline && isGray) {462markupType = kRawText;463} else if (isThickUnderline && !isGray) {464markupType = kSelectedConvertedText;465} else if (!isThickUnderline && !isGray) {466markupType = kConvertedText;467}468469InputMethodHighlight theHighlight;470471switch (markupType) {472case kSelectedRawText:473theHighlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;474break;475case kConvertedText:476theHighlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;477break;478case kSelectedConvertedText:479theHighlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;480break;481case kRawText:482default:483theHighlight = InputMethodHighlight.UNSELECTED_RAW_TEXT_HIGHLIGHT;484break;485}486487fCurrentText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, theHighlight, begin, end);488}489490/* Called from JNI to select the previously typed glyph during press and hold */491private void selectPreviousGlyph() {492if (fIMContext == null) return; // ???493try {494LWCToolkit.invokeLater(new Runnable() {495public void run() {496final int offset = fIMContext.getInsertPositionOffset();497if (offset < 1) return; // ???498499if (fAwtFocussedComponent instanceof JTextComponent) {500((JTextComponent) fAwtFocussedComponent).select(offset - 1, offset);501return;502}503504if (fAwtFocussedComponent instanceof TextComponent) {505((TextComponent) fAwtFocussedComponent).select(offset - 1, offset);506return;507}508// TODO: Ideally we want to disable press-and-hold in this case509}510}, fAwtFocussedComponent);511} catch (Exception e) {512e.printStackTrace();513}514}515516private void selectNextGlyph() {517if (fIMContext == null || !(fAwtFocussedComponent instanceof JTextComponent)) return;518try {519LWCToolkit.invokeLater(new Runnable() {520public void run() {521final int offset = fIMContext.getInsertPositionOffset();522if (offset < 0) return;523((JTextComponent) fAwtFocussedComponent).select(offset, offset + 1);524return;525}526}, fAwtFocussedComponent);527} catch (Exception e) {528e.printStackTrace();529}530}531532private void dispatchText(int selectStart, int selectLength, boolean pressAndHold) {533// Nothing to do if we have no text.534if (fCurrentText == null)535return;536537TextHitInfo theCaret = (selectLength == 0 ? TextHitInfo.beforeOffset(selectStart) : null);538TextHitInfo visiblePosition = TextHitInfo.beforeOffset(0);539540InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,541InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,542fCurrentText.getIterator(),5430,544theCaret,545visiblePosition);546LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);547548if (pressAndHold) selectNextGlyph();549}550551/**552* Frequent callbacks from NSTextInput. I think we're supposed to commit it here?553*/554synchronized private void unmarkText() {555if (fCurrentText == null)556return;557558TextHitInfo theCaret = TextHitInfo.afterOffset(fCurrentTextLength);559TextHitInfo visiblePosition = theCaret;560InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,561InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,562fCurrentText.getIterator(),563fCurrentTextLength,564theCaret,565visiblePosition);566LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);567fCurrentText = null;568fCurrentTextAsString = null;569fCurrentTextLength = 0;570}571572synchronized private boolean hasMarkedText() {573return fCurrentText != null;574}575576/**577* Cocoa assumes the marked text and committed text is all stored in the same storage, but578* Java does not. So, we have to see where the request is and based on that return the right579* substring.580*/581synchronized private String attributedSubstringFromRange(final int locationIn, final int lengthIn) {582final String[] retString = new String[1];583584try {585LWCToolkit.invokeAndWait(new Runnable() {586public void run() { synchronized(retString) {587int location = locationIn;588int length = lengthIn;589590if ((location + length) > (fIMContext.getCommittedTextLength() + fCurrentTextLength)) {591length = fIMContext.getCommittedTextLength() - location;592}593594AttributedCharacterIterator theIterator = null;595596if (fCurrentText == null) {597theIterator = fIMContext.getCommittedText(location, location + length, null);598} else {599int insertSpot = fIMContext.getInsertPositionOffset();600601if (location < insertSpot) {602theIterator = fIMContext.getCommittedText(location, location + length, null);603} else if (location >= insertSpot && location < insertSpot + fCurrentTextLength) {604theIterator = fCurrentText.getIterator(null, location - insertSpot, location - insertSpot +length);605} else {606theIterator = fIMContext.getCommittedText(location - fCurrentTextLength, location - fCurrentTextLength + length, null);607}608}609610// Get the characters from the iterator611char selectedText[] = new char[theIterator.getEndIndex() - theIterator.getBeginIndex()];612char current = theIterator.first();613int index = 0;614while (current != CharacterIterator.DONE) {615selectedText[index++] = current;616current = theIterator.next();617}618619retString[0] = new String(selectedText);620}}621}, fAwtFocussedComponent);622} catch (InvocationTargetException ite) { ite.printStackTrace(); }623624synchronized(retString) { return retString[0]; }625}626627/**628* Cocoa wants the range of characters that are currently selected. We have to synthesize this629* by getting the insert location and the length of the selected text. NB: This does NOT allow630* for the fact that the insert point in Swing can come AFTER the selected text, making this631* potentially incorrect.632*/633synchronized private int[] selectedRange() {634final int[] returnValue = new int[2];635636try {637LWCToolkit.invokeAndWait(new Runnable() {638public void run() { synchronized(returnValue) {639AttributedCharacterIterator theIterator = fIMContext.getSelectedText(null);640if (theIterator == null) {641returnValue[0] = fIMContext.getInsertPositionOffset();642returnValue[1] = 0;643return;644}645646int startLocation;647648if (fAwtFocussedComponent instanceof JTextComponent) {649JTextComponent theComponent = (JTextComponent)fAwtFocussedComponent;650startLocation = theComponent.getSelectionStart();651} else if (fAwtFocussedComponent instanceof TextComponent) {652TextComponent theComponent = (TextComponent)fAwtFocussedComponent;653startLocation = theComponent.getSelectionStart();654} else {655// If we don't have a Swing or AWT component, we have to guess whether the selection is before or after the input spot.656startLocation = fIMContext.getInsertPositionOffset() - (theIterator.getEndIndex() - theIterator.getBeginIndex());657658// If the calculated spot is negative the insert spot must be at the beginning of659// the selection.660if (startLocation < 0) {661startLocation = fIMContext.getInsertPositionOffset() + (theIterator.getEndIndex() - theIterator.getBeginIndex());662}663}664665returnValue[0] = startLocation;666returnValue[1] = theIterator.getEndIndex() - theIterator.getBeginIndex();667668}}669}, fAwtFocussedComponent);670} catch (InvocationTargetException ite) { ite.printStackTrace(); }671672synchronized(returnValue) { return returnValue; }673}674675/**676* Cocoa wants the range of characters that are currently marked. Since Java doesn't store committed and677* text in progress (composed text) together, we have to synthesize it. We know where the text will be678* inserted, so we can return that position, and the length of the text in progress. If there is no marked text679* return null.680*/681synchronized private int[] markedRange() {682if (fCurrentText == null)683return null;684685final int[] returnValue = new int[2];686687try {688LWCToolkit.invokeAndWait(new Runnable() {689public void run() { synchronized(returnValue) {690// The insert position is always after the composed text, so the range start is the691// insert spot less the length of the composed text.692returnValue[0] = fIMContext.getInsertPositionOffset();693}}694}, fAwtFocussedComponent);695} catch (InvocationTargetException ite) { ite.printStackTrace(); }696697returnValue[1] = fCurrentTextLength;698synchronized(returnValue) { return returnValue; }699}700701/**702* Cocoa wants a rectangle that describes where a particular range is on screen, but only cares about the703* location of that rectangle. We are given the index of the character for which we want the location on704* screen, which will be a character in the in-progress text. By subtracting the current insert position,705* which is always in front of the in-progress text, we get the offset into the composed text, and we get706* that location from the input method context.707*/708synchronized private int[] firstRectForCharacterRange(final int absoluteTextOffset) {709final int[] rect = new int[4];710711try {712LWCToolkit.invokeAndWait(new Runnable() {713public void run() { synchronized(rect) {714int insertOffset = fIMContext.getInsertPositionOffset();715int composedTextOffset = absoluteTextOffset - insertOffset;716if (composedTextOffset < 0) composedTextOffset = 0;717Rectangle r = fIMContext.getTextLocation(TextHitInfo.beforeOffset(composedTextOffset));718rect[0] = r.x;719rect[1] = r.y;720rect[2] = r.width;721rect[3] = r.height;722723// This next if-block is a hack to work around a bug in JTextComponent. getTextLocation ignores724// the TextHitInfo passed to it and always returns the location of the insertion point, which is725// at the start of the composed text. We'll do some calculation so the candidate window for Kotoeri726// follows the requested offset into the composed text.727if (composedTextOffset > 0 && (fAwtFocussedComponent instanceof JTextComponent)) {728Rectangle r2 = fIMContext.getTextLocation(TextHitInfo.beforeOffset(0));729730if (r.equals(r2)) {731// FIXME: (SAK) If the candidate text wraps over two lines, this calculation pushes the candidate732// window off the right edge of the component.733String inProgressSubstring = fCurrentTextAsString.substring(0, composedTextOffset);734Graphics g = fAwtFocussedComponent.getGraphics();735int xOffset = g.getFontMetrics().stringWidth(inProgressSubstring);736rect[0] += xOffset;737g.dispose();738}739}740}}741}, fAwtFocussedComponent);742} catch (InvocationTargetException ite) { ite.printStackTrace(); }743744synchronized(rect) { return rect; }745}746747/* This method returns the index for the character that is nearest to the point described by screenX and screenY.748* The coordinates are in Java screen coordinates. If no character in the composed text was hit, we return -1, indicating749* not found.750*/751synchronized private int characterIndexForPoint(final int screenX, final int screenY) {752final TextHitInfo[] offsetInfo = new TextHitInfo[1];753final int[] insertPositionOffset = new int[1];754755try {756LWCToolkit.invokeAndWait(new Runnable() {757public void run() { synchronized(offsetInfo) {758offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);759insertPositionOffset[0] = fIMContext.getInsertPositionOffset();760}}761}, fAwtFocussedComponent);762} catch (InvocationTargetException ite) { ite.printStackTrace(); }763764// This bit of gymnastics ensures that the returned location is within the composed text.765// If it falls outside that region, the input method will commit the text, which is inconsistent with native766// Cocoa apps (see TextEdit, for example.) Clicking to the left of or above the selected text moves the767// cursor to the start of the composed text, and to the right or below moves it to one character before the end.768if (offsetInfo[0] == null) {769return insertPositionOffset[0];770}771772int returnValue = offsetInfo[0].getCharIndex() + insertPositionOffset[0];773774if (offsetInfo[0].getCharIndex() == fCurrentTextLength)775returnValue --;776777return returnValue;778}779780// On Mac OS X we effectively disabled the input method when focus was lost, so781// this call can be ignored.782public void disableInputMethod()783{784// Deliberately ignored. See setAWTFocussedComponent above.785}786787public String getNativeInputMethodInfo()788{789return nativeGetCurrentInputMethodInfo();790}791792793// =========================== Native methods ===========================794// Note that if nativePeer isn't something that normally accepts keystrokes (i.e., a CPanel)795// these calls will be ignored.796private native void nativeNotifyPeer(long nativePeer, CInputMethod imInstance);797private native void nativeEndComposition(long nativePeer);798private native void nativeHandleEvent(LWComponentPeer peer, AWTEvent event);799800// Returns the locale of the active input method.801static native Locale getNativeLocale();802803// Switches to the input method with language indicated in localeName804static native boolean setNativeLocale(String localeName, boolean onActivate);805806// Returns information about the currently selected input method.807static native String nativeGetCurrentInputMethodInfo();808809// Initialize toolbox routines810static native void nativeInit();811}812813814