blob: ad9d71be7c0d17b0dc554eb1288e86d76f0f7ccc [file] [log] [blame]
/*
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.awt.im;
import java.awt.AWTEvent;
import java.awt.AWTKeyStroke;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.im.InputMethodRequests;
import java.awt.im.spi.InputMethod;
import java.lang.Character.Subset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import sun.util.logging.PlatformLogger;
import sun.awt.SunToolkit;
/**
* This InputContext class contains parts of the implementation of
* java.text.im.InputContext. These parts have been moved
* here to avoid exposing protected members that are needed by the
* subclass InputMethodContext.
*
* @see java.awt.im.InputContext
* @author JavaSoft Asia/Pacific
*/
public class InputContext extends java.awt.im.InputContext
implements ComponentListener, WindowListener {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.im.InputContext");
// The current input method is represented by two objects:
// a locator is used to keep information about the selected
// input method and locale until we actually need a real input
// method; only then the input method itself is created.
// Once there is an input method, the input method's locale
// takes precedence over locale information in the locator.
private InputMethodLocator inputMethodLocator;
private InputMethod inputMethod;
private boolean inputMethodCreationFailed;
// holding bin for previously used input method instances, but not the current one
private HashMap<InputMethodLocator, InputMethod> usedInputMethods;
// the current client component is kept until the user focusses on a different
// client component served by the same input context. When that happens, we call
// endComposition so that text doesn't jump from one component to another.
private Component currentClientComponent;
private Component awtFocussedComponent;
private boolean isInputMethodActive;
private Subset[] characterSubsets = null;
// true if composition area has been set to invisible when focus was lost
private boolean compositionAreaHidden = false;
// The input context for whose input method we may have to call hideWindows
private static InputContext inputMethodWindowContext;
// Previously active input method to decide whether we need to call
// InputMethodAdapter.stopListening() on activateInputMethod()
private static InputMethod previousInputMethod = null;
// true if the current input method requires client window change notification
private boolean clientWindowNotificationEnabled = false;
// client window to which this input context is listening
private Window clientWindowListened;
// cache location notification
private Rectangle clientWindowLocation = null;
// holding the state of clientWindowNotificationEnabled of only non-current input methods
private HashMap<InputMethod, Boolean> perInputMethodState;
// Input Method selection hot key stuff
private static AWTKeyStroke inputMethodSelectionKey;
private static boolean inputMethodSelectionKeyInitialized = false;
private static final String inputMethodSelectionKeyPath = "/java/awt/im/selectionKey";
private static final String inputMethodSelectionKeyCodeName = "keyCode";
private static final String inputMethodSelectionKeyModifiersName = "modifiers";
/**
* Constructs an InputContext.
*/
protected InputContext() {
InputMethodManager imm = InputMethodManager.getInstance();
synchronized (InputContext.class) {
if (!inputMethodSelectionKeyInitialized) {
inputMethodSelectionKeyInitialized = true;
if (imm.hasMultipleInputMethods()) {
initializeInputMethodSelectionKey();
}
}
}
selectInputMethod(imm.getDefaultKeyboardLocale());
}
/**
* @see java.awt.im.InputContext#selectInputMethod
* @exception NullPointerException when the locale is null.
*/
public synchronized boolean selectInputMethod(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
// see whether the current input method supports the locale
if (inputMethod != null) {
if (inputMethod.setLocale(locale)) {
return true;
}
} else if (inputMethodLocator != null) {
// This is not 100% correct, since the input method
// may support the locale without advertising it.
// But before we try instantiations and setLocale,
// we look for an input method that's more confident.
if (inputMethodLocator.isLocaleAvailable(locale)) {
inputMethodLocator = inputMethodLocator.deriveLocator(locale);
return true;
}
}
// see whether there's some other input method that supports the locale
InputMethodLocator newLocator = InputMethodManager.getInstance().findInputMethod(locale);
if (newLocator != null) {
changeInputMethod(newLocator);
return true;
}
// make one last desperate effort with the current input method
// ??? is this good? This is pretty high cost for something that's likely to fail.
if (inputMethod == null && inputMethodLocator != null) {
inputMethod = getInputMethod();
if (inputMethod != null) {
return inputMethod.setLocale(locale);
}
}
return false;
}
/**
* @see java.awt.im.InputContext#getLocale
*/
public Locale getLocale() {
if (inputMethod != null) {
return inputMethod.getLocale();
} else if (inputMethodLocator != null) {
return inputMethodLocator.getLocale();
} else {
return null;
}
}
/**
* @see java.awt.im.InputContext#setCharacterSubsets
*/
public void setCharacterSubsets(Subset[] subsets) {
if (subsets == null) {
characterSubsets = null;
} else {
characterSubsets = new Subset[subsets.length];
System.arraycopy(subsets, 0,
characterSubsets, 0, characterSubsets.length);
}
if (inputMethod != null) {
inputMethod.setCharacterSubsets(subsets);
}
}
/**
* @see java.awt.im.InputContext#reconvert
* @since 1.3
* @exception UnsupportedOperationException when input method is null
*/
public synchronized void reconvert() {
InputMethod inputMethod = getInputMethod();
if (inputMethod == null) {
throw new UnsupportedOperationException();
}
inputMethod.reconvert();
}
/**
* @see java.awt.im.InputContext#dispatchEvent
*/
@SuppressWarnings("fallthrough")
public void dispatchEvent(AWTEvent event) {
if (event instanceof InputMethodEvent) {
return;
}
// Ignore focus events that relate to the InputMethodWindow of this context.
// This is a workaround. Should be removed after 4452384 is fixed.
if (event instanceof FocusEvent) {
Component opposite = ((FocusEvent)event).getOppositeComponent();
if ((opposite != null) &&
(getComponentWindow(opposite) instanceof InputMethodWindow) &&
(opposite.getInputContext() == this)) {
return;
}
}
InputMethod inputMethod = getInputMethod();
int id = event.getID();
switch (id) {
case FocusEvent.FOCUS_GAINED:
focusGained((Component) event.getSource());
break;
case FocusEvent.FOCUS_LOST:
focusLost((Component) event.getSource(), ((FocusEvent) event).isTemporary());
break;
case KeyEvent.KEY_PRESSED:
if (checkInputMethodSelectionKey((KeyEvent)event)) {
// pop up the input method selection menu
InputMethodManager.getInstance().notifyChangeRequestByHotKey((Component)event.getSource());
break;
}
// fall through
default:
if ((inputMethod != null) && (event instanceof InputEvent)) {
inputMethod.dispatchEvent(event);
}
}
}
/**
* Handles focus gained events for any component that's using
* this input context.
* These events are generated by AWT when the keyboard focus
* moves to a component.
* Besides actual client components, the source components
* may also be the composition area or any component in an
* input method window.
* <p>
* When handling the focus event for a client component, this
* method checks whether the input context was previously
* active for a different client component, and if so, calls
* endComposition for the previous client component.
*
* @param source the component gaining the focus
*/
private void focusGained(Component source) {
/*
* NOTE: When a Container is removing its Component which
* invokes this.removeNotify(), the Container has the global
* Component lock. It is possible to happen that an
* application thread is calling this.removeNotify() while an
* AWT event queue thread is dispatching a focus event via
* this.dispatchEvent(). If an input method uses AWT
* components (e.g., IIIMP status window), it causes deadlock,
* for example, Component.show()/hide() in this situation
* because hide/show tried to obtain the lock. Therefore,
* it's necessary to obtain the global Component lock before
* activating or deactivating an input method.
*/
synchronized (source.getTreeLock()) {
synchronized (this) {
if ("sun.awt.im.CompositionArea".equals(source.getClass().getName())) {
// no special handling for this one
} else if (getComponentWindow(source) instanceof InputMethodWindow) {
// no special handling for this one either
} else {
if (!source.isDisplayable()) {
// Component is being disposed
return;
}
// Focus went to a real client component.
// Check whether we're switching between client components
// that share an input context. We can't do that earlier
// than here because we don't want to end composition
// until we really know we're switching to a different component
if (inputMethod != null) {
if (currentClientComponent != null && currentClientComponent != source) {
if (!isInputMethodActive) {
activateInputMethod(false);
}
endComposition();
deactivateInputMethod(false);
}
}
currentClientComponent = source;
}
awtFocussedComponent = source;
if (inputMethod instanceof InputMethodAdapter) {
((InputMethodAdapter) inputMethod).setAWTFocussedComponent(source);
}
// it's possible that the input method is still active because
// we suppressed a deactivate cause by an input method window
// coming up
if (!isInputMethodActive) {
activateInputMethod(true);
}
// If the client component is an active client with the below-the-spot
// input style, then make the composition window undecorated without a title bar.
InputMethodContext inputContext = ((InputMethodContext)this);
if (!inputContext.isCompositionAreaVisible()) {
InputMethodRequests req = source.getInputMethodRequests();
if (req != null && inputContext.useBelowTheSpotInput()) {
inputContext.setCompositionAreaUndecorated(true);
} else {
inputContext.setCompositionAreaUndecorated(false);
}
}
// restores the composition area if it was set to invisible
// when focus got lost
if (compositionAreaHidden == true) {
((InputMethodContext)this).setCompositionAreaVisible(true);
compositionAreaHidden = false;
}
}
}
}
/**
* Activates the current input method of this input context, and grabs
* the composition area for use by this input context.
* If updateCompositionArea is true, the text in the composition area
* is updated (set to false if the text is going to change immediately
* to avoid screen flicker).
*/
private void activateInputMethod(boolean updateCompositionArea) {
// call hideWindows() if this input context uses a different
// input method than the previously activated one
if (inputMethodWindowContext != null && inputMethodWindowContext != this &&
inputMethodWindowContext.inputMethodLocator != null &&
!inputMethodWindowContext.inputMethodLocator.sameInputMethod(inputMethodLocator) &&
inputMethodWindowContext.inputMethod != null) {
inputMethodWindowContext.inputMethod.hideWindows();
}
inputMethodWindowContext = this;
if (inputMethod != null) {
if (previousInputMethod != inputMethod &&
previousInputMethod instanceof InputMethodAdapter) {
// let the host adapter pass through the input events for the
// new input method
((InputMethodAdapter) previousInputMethod).stopListening();
}
previousInputMethod = null;
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Current client component " + currentClientComponent);
}
if (inputMethod instanceof InputMethodAdapter) {
((InputMethodAdapter) inputMethod).setClientComponent(currentClientComponent);
}
inputMethod.activate();
isInputMethodActive = true;
if (perInputMethodState != null) {
Boolean state = perInputMethodState.remove(inputMethod);
if (state != null) {
clientWindowNotificationEnabled = state.booleanValue();
}
}
if (clientWindowNotificationEnabled) {
if (!addedClientWindowListeners()) {
addClientWindowListeners();
}
synchronized(this) {
if (clientWindowListened != null) {
notifyClientWindowChange(clientWindowListened);
}
}
} else {
if (addedClientWindowListeners()) {
removeClientWindowListeners();
}
}
}
InputMethodManager.getInstance().setInputContext(this);
((InputMethodContext) this).grabCompositionArea(updateCompositionArea);
}
static Window getComponentWindow(Component component) {
while (true) {
if (component == null) {
return null;
} else if (component instanceof Window) {
return (Window) component;
} else {
component = component.getParent();
}
}
}
/**
* Handles focus lost events for any component that's using
* this input context.
* These events are generated by AWT when the keyboard focus
* moves away from a component.
* Besides actual client components, the source components
* may also be the composition area or any component in an
* input method window.
*
* @param source the component losing the focus
* @isTemporary whether the focus change is temporary
*/
private void focusLost(Component source, boolean isTemporary) {
// see the note on synchronization in focusGained
synchronized (source.getTreeLock()) {
synchronized (this) {
// We need to suppress deactivation if removeNotify has been called earlier.
// This is indicated by isInputMethodActive == false.
if (isInputMethodActive) {
deactivateInputMethod(isTemporary);
}
awtFocussedComponent = null;
if (inputMethod instanceof InputMethodAdapter) {
((InputMethodAdapter) inputMethod).setAWTFocussedComponent(null);
}
// hides the composition area if currently it is visible
InputMethodContext inputContext = ((InputMethodContext)this);
if (inputContext.isCompositionAreaVisible()) {
inputContext.setCompositionAreaVisible(false);
compositionAreaHidden = true;
}
}
}
}
/**
* Checks the key event is the input method selection key or not.
*/
private boolean checkInputMethodSelectionKey(KeyEvent event) {
if (inputMethodSelectionKey != null) {
AWTKeyStroke aKeyStroke = AWTKeyStroke.getAWTKeyStrokeForEvent(event);
return inputMethodSelectionKey.equals(aKeyStroke);
} else {
return false;
}
}
private void deactivateInputMethod(boolean isTemporary) {
InputMethodManager.getInstance().setInputContext(null);
if (inputMethod != null) {
isInputMethodActive = false;
inputMethod.deactivate(isTemporary);
previousInputMethod = inputMethod;
}
}
/**
* Switches from the current input method to the one described by newLocator.
* The current input method, if any, is asked to end composition, deactivated,
* and saved for future use. The newLocator is made the current locator. If
* the input context is active, an input method instance for the new locator
* is obtained; otherwise this is deferred until required.
*/
synchronized void changeInputMethod(InputMethodLocator newLocator) {
// If we don't have a locator yet, this must be a new input context.
// If we created a new input method here, we might get into an
// infinite loop: create input method -> create some input method window ->
// create new input context -> add input context to input method manager's context list ->
// call changeInputMethod on it.
// So, just record the locator. dispatchEvent will create the input method when needed.
if (inputMethodLocator == null) {
inputMethodLocator = newLocator;
inputMethodCreationFailed = false;
return;
}
// If the same input method is specified, just keep it.
// Adjust the locale if necessary.
if (inputMethodLocator.sameInputMethod(newLocator)) {
Locale newLocale = newLocator.getLocale();
if (newLocale != null && inputMethodLocator.getLocale() != newLocale) {
if (inputMethod != null) {
inputMethod.setLocale(newLocale);
}
inputMethodLocator = newLocator;
}
return;
}
// Switch out the old input method
Locale savedLocale = inputMethodLocator.getLocale();
boolean wasInputMethodActive = isInputMethodActive;
boolean wasCompositionEnabledSupported = false;
boolean wasCompositionEnabled = false;
if (inputMethod != null) {
try {
wasCompositionEnabled = inputMethod.isCompositionEnabled();
wasCompositionEnabledSupported = true;
} catch (UnsupportedOperationException e) { }
if (currentClientComponent != null) {
if (!isInputMethodActive) {
activateInputMethod(false);
}
endComposition();
deactivateInputMethod(false);
if (inputMethod instanceof InputMethodAdapter) {
((InputMethodAdapter) inputMethod).setClientComponent(null);
}
if (null == currentClientComponent.getInputMethodRequests())
wasCompositionEnabledSupported = false;
}
savedLocale = inputMethod.getLocale();
// keep the input method instance around for future use
if (usedInputMethods == null) {
usedInputMethods = new HashMap<>(5);
}
if (perInputMethodState == null) {
perInputMethodState = new HashMap<>(5);
}
usedInputMethods.put(inputMethodLocator.deriveLocator(null), inputMethod);
perInputMethodState.put(inputMethod,
Boolean.valueOf(clientWindowNotificationEnabled));
enableClientWindowNotification(inputMethod, false);
if (this == inputMethodWindowContext) {
inputMethod.hideWindows();
inputMethod.removeNotify();
inputMethodWindowContext = null;
}
inputMethodLocator = null;
inputMethod = null;
inputMethodCreationFailed = false;
}
// Switch in the new input method
if (newLocator.getLocale() == null && savedLocale != null &&
newLocator.isLocaleAvailable(savedLocale)) {
newLocator = newLocator.deriveLocator(savedLocale);
}
inputMethodLocator = newLocator;
inputMethodCreationFailed = false;
// activate the new input method if the old one was active
if (wasInputMethodActive) {
inputMethod = getInputMethodInstance();
if (inputMethod instanceof InputMethodAdapter) {
((InputMethodAdapter) inputMethod).setAWTFocussedComponent(awtFocussedComponent);
}
activateInputMethod(true);
}
// enable/disable composition if the old one supports querying enable/disable
if (wasCompositionEnabledSupported) {
inputMethod = getInputMethod();
if (inputMethod != null) {
try {
inputMethod.setCompositionEnabled(wasCompositionEnabled);
} catch (UnsupportedOperationException e) { }
}
}
}
/**
* Returns the client component.
*/
Component getClientComponent() {
return currentClientComponent;
}
/**
* @see java.awt.im.InputContext#removeNotify
* @exception NullPointerException when the component is null.
*/
public synchronized void removeNotify(Component component) {
if (component == null) {
throw new NullPointerException();
}
if (inputMethod == null) {
if (component == currentClientComponent) {
currentClientComponent = null;
}
return;
}
// We may or may not get a FOCUS_LOST event for this component,
// so do the deactivation stuff here too.
if (component == awtFocussedComponent) {
focusLost(component, false);
}
if (component == currentClientComponent) {
if (isInputMethodActive) {
// component wasn't the one that had the focus
deactivateInputMethod(false);
}
inputMethod.removeNotify();
if (clientWindowNotificationEnabled && addedClientWindowListeners()) {
removeClientWindowListeners();
}
currentClientComponent = null;
if (inputMethod instanceof InputMethodAdapter) {
((InputMethodAdapter) inputMethod).setClientComponent(null);
}
// removeNotify() can be issued from a thread other than the event dispatch
// thread. In that case, avoid possible deadlock between Component.AWTTreeLock
// and InputMethodContext.compositionAreaHandlerLock by releasing the composition
// area on the event dispatch thread.
if (EventQueue.isDispatchThread()) {
((InputMethodContext)this).releaseCompositionArea();
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
((InputMethodContext)InputContext.this).releaseCompositionArea();
}
});
}
}
}
/**
* @see java.awt.im.InputContext#dispose
* @exception IllegalStateException when the currentClientComponent is not null
*/
public synchronized void dispose() {
if (currentClientComponent != null) {
throw new IllegalStateException("Can't dispose InputContext while it's active");
}
if (inputMethod != null) {
if (this == inputMethodWindowContext) {
inputMethod.hideWindows();
inputMethodWindowContext = null;
}
if (inputMethod == previousInputMethod) {
previousInputMethod = null;
}
if (clientWindowNotificationEnabled) {
if (addedClientWindowListeners()) {
removeClientWindowListeners();
}
clientWindowNotificationEnabled = false;
}
inputMethod.dispose();
// in case the input method enabled the client window
// notification in dispose(), which shouldn't happen, it
// needs to be cleaned up again.
if (clientWindowNotificationEnabled) {
enableClientWindowNotification(inputMethod, false);
}
inputMethod = null;
}
inputMethodLocator = null;
if (usedInputMethods != null && !usedInputMethods.isEmpty()) {
Iterator<InputMethod> iterator = usedInputMethods.values().iterator();
usedInputMethods = null;
while (iterator.hasNext()) {
iterator.next().dispose();
}
}
// cleanup client window notification variables
clientWindowNotificationEnabled = false;
clientWindowListened = null;
perInputMethodState = null;
}
/**
* @see java.awt.im.InputContext#getInputMethodControlObject
*/
public synchronized Object getInputMethodControlObject() {
InputMethod inputMethod = getInputMethod();
if (inputMethod != null) {
return inputMethod.getControlObject();
} else {
return null;
}
}
/**
* @see java.awt.im.InputContext#setCompositionEnabled(boolean)
* @exception UnsupportedOperationException when input method is null
*/
public void setCompositionEnabled(boolean enable) {
InputMethod inputMethod = getInputMethod();
if (inputMethod == null) {
throw new UnsupportedOperationException();
}
inputMethod.setCompositionEnabled(enable);
}
/**
* @see java.awt.im.InputContext#isCompositionEnabled
* @exception UnsupportedOperationException when input method is null
*/
public boolean isCompositionEnabled() {
InputMethod inputMethod = getInputMethod();
if (inputMethod == null) {
throw new UnsupportedOperationException();
}
return inputMethod.isCompositionEnabled();
}
/**
* @return a string with information about the current input method.
* @exception UnsupportedOperationException when input method is null
*/
public String getInputMethodInfo() {
InputMethod inputMethod = getInputMethod();
if (inputMethod == null) {
throw new UnsupportedOperationException("Null input method");
}
String inputMethodInfo = null;
if (inputMethod instanceof InputMethodAdapter) {
// returns the information about the host native input method.
inputMethodInfo = ((InputMethodAdapter)inputMethod).
getNativeInputMethodInfo();
}
// extracts the information from the InputMethodDescriptor
// associated with the current java input method.
if (inputMethodInfo == null && inputMethodLocator != null) {
inputMethodInfo = inputMethodLocator.getDescriptor().
getInputMethodDisplayName(getLocale(), SunToolkit.
getStartupLocale());
}
if (inputMethodInfo != null && !inputMethodInfo.equals("")) {
return inputMethodInfo;
}
// do our best to return something useful.
return inputMethod.toString() + "-" + inputMethod.getLocale().toString();
}
/**
* Turns off the native IM. The native IM is diabled when
* the deactive method of InputMethod is called. It is
* delayed until the active method is called on a different
* peer component. This method is provided to explicitly disable
* the native IM.
*/
public void disableNativeIM() {
InputMethod inputMethod = getInputMethod();
if (inputMethod != null && inputMethod instanceof InputMethodAdapter) {
((InputMethodAdapter)inputMethod).stopListening();
}
}
private synchronized InputMethod getInputMethod() {
if (inputMethod != null) {
return inputMethod;
}
if (inputMethodCreationFailed) {
return null;
}
inputMethod = getInputMethodInstance();
return inputMethod;
}
/**
* Returns an instance of the input method described by
* the current input method locator. This may be an input
* method that was previously used and switched out of,
* or a new instance. The locale, character subsets, and
* input method context of the input method are set.
*
* The inputMethodCreationFailed field is set to true if the
* instantiation failed.
*
* @return an InputMethod instance
* @see java.awt.im.spi.InputMethod#setInputMethodContext
* @see java.awt.im.spi.InputMethod#setLocale
* @see java.awt.im.spi.InputMethod#setCharacterSubsets
*/
private InputMethod getInputMethodInstance() {
InputMethodLocator locator = inputMethodLocator;
if (locator == null) {
inputMethodCreationFailed = true;
return null;
}
Locale locale = locator.getLocale();
InputMethod inputMethodInstance = null;
// see whether we have a previously used input method
if (usedInputMethods != null) {
inputMethodInstance = usedInputMethods.remove(locator.deriveLocator(null));
if (inputMethodInstance != null) {
if (locale != null) {
inputMethodInstance.setLocale(locale);
}
inputMethodInstance.setCharacterSubsets(characterSubsets);
Boolean state = perInputMethodState.remove(inputMethodInstance);
if (state != null) {
enableClientWindowNotification(inputMethodInstance, state.booleanValue());
}
((InputMethodContext) this).setInputMethodSupportsBelowTheSpot(
(!(inputMethodInstance instanceof InputMethodAdapter)) ||
((InputMethodAdapter) inputMethodInstance).supportsBelowTheSpot());
return inputMethodInstance;
}
}
// need to create new instance
try {
inputMethodInstance = locator.getDescriptor().createInputMethod();
if (locale != null) {
inputMethodInstance.setLocale(locale);
}
inputMethodInstance.setInputMethodContext((InputMethodContext) this);
inputMethodInstance.setCharacterSubsets(characterSubsets);
} catch (Exception e) {
logCreationFailed(e);
// there are a number of bad things that can happen while creating
// the input method. In any case, we just continue without an
// input method.
inputMethodCreationFailed = true;
// if the instance has been created, then it means either
// setLocale() or setInputMethodContext() failed.
if (inputMethodInstance != null) {
inputMethodInstance = null;
}
} catch (LinkageError e) {
logCreationFailed(e);
// same as above
inputMethodCreationFailed = true;
}
((InputMethodContext) this).setInputMethodSupportsBelowTheSpot(
(!(inputMethodInstance instanceof InputMethodAdapter)) ||
((InputMethodAdapter) inputMethodInstance).supportsBelowTheSpot());
return inputMethodInstance;
}
private void logCreationFailed(Throwable throwable) {
PlatformLogger logger = PlatformLogger.getLogger("sun.awt.im");
if (logger.isLoggable(PlatformLogger.Level.CONFIG)) {
String errorTextFormat = Toolkit.getProperty("AWT.InputMethodCreationFailed",
"Could not create {0}. Reason: {1}");
Object[] args =
{inputMethodLocator.getDescriptor().getInputMethodDisplayName(null, Locale.getDefault()),
throwable.getLocalizedMessage()};
MessageFormat mf = new MessageFormat(errorTextFormat);
logger.config(mf.format(args));
}
}
InputMethodLocator getInputMethodLocator() {
if (inputMethod != null) {
return inputMethodLocator.deriveLocator(inputMethod.getLocale());
}
return inputMethodLocator;
}
/**
* @see java.awt.im.InputContext#endComposition
*/
public synchronized void endComposition() {
if (inputMethod != null) {
inputMethod.endComposition();
}
}
/**
* @see java.awt.im.spi.InputMethodContext#enableClientWindowNotification
*/
synchronized void enableClientWindowNotification(InputMethod requester,
boolean enable) {
// in case this request is not from the current input method,
// store the request and handle it when this requesting input
// method becomes the current one.
if (requester != inputMethod) {
if (perInputMethodState == null) {
perInputMethodState = new HashMap<>(5);
}
perInputMethodState.put(requester, Boolean.valueOf(enable));
return;
}
if (clientWindowNotificationEnabled != enable) {
clientWindowLocation = null;
clientWindowNotificationEnabled = enable;
}
if (clientWindowNotificationEnabled) {
if (!addedClientWindowListeners()) {
addClientWindowListeners();
}
if (clientWindowListened != null) {
clientWindowLocation = null;
notifyClientWindowChange(clientWindowListened);
}
} else {
if (addedClientWindowListeners()) {
removeClientWindowListeners();
}
}
}
private synchronized void notifyClientWindowChange(Window window) {
if (inputMethod == null) {
return;
}
// if the window is invisible or iconified, send null to the input method.
if (!window.isVisible() ||
((window instanceof Frame) && ((Frame)window).getState() == Frame.ICONIFIED)) {
clientWindowLocation = null;
inputMethod.notifyClientWindowChange(null);
return;
}
Rectangle location = window.getBounds();
if (clientWindowLocation == null || !clientWindowLocation.equals(location)) {
clientWindowLocation = location;
inputMethod.notifyClientWindowChange(clientWindowLocation);
}
}
private synchronized void addClientWindowListeners() {
Component client = getClientComponent();
if (client == null) {
return;
}
Window window = getComponentWindow(client);
if (window == null) {
return;
}
window.addComponentListener(this);
window.addWindowListener(this);
clientWindowListened = window;
}
private synchronized void removeClientWindowListeners() {
clientWindowListened.removeComponentListener(this);
clientWindowListened.removeWindowListener(this);
clientWindowListened = null;
}
/**
* Returns true if listeners have been set up for client window
* change notification.
*/
private boolean addedClientWindowListeners() {
return clientWindowListened != null;
}
/*
* ComponentListener and WindowListener implementation
*/
public void componentResized(ComponentEvent e) {
notifyClientWindowChange((Window)e.getComponent());
}
public void componentMoved(ComponentEvent e) {
notifyClientWindowChange((Window)e.getComponent());
}
public void componentShown(ComponentEvent e) {
notifyClientWindowChange((Window)e.getComponent());
}
public void componentHidden(ComponentEvent e) {
notifyClientWindowChange((Window)e.getComponent());
}
public void windowOpened(WindowEvent e) {}
public void windowClosing(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {
notifyClientWindowChange(e.getWindow());
}
public void windowDeiconified(WindowEvent e) {
notifyClientWindowChange(e.getWindow());
}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
/**
* Initializes the input method selection key definition in preference trees
*/
private void initializeInputMethodSelectionKey() {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
// Look in user's tree
Preferences root = Preferences.userRoot();
inputMethodSelectionKey = getInputMethodSelectionKeyStroke(root);
if (inputMethodSelectionKey == null) {
// Look in system's tree
root = Preferences.systemRoot();
inputMethodSelectionKey = getInputMethodSelectionKeyStroke(root);
}
return null;
}
});
}
private AWTKeyStroke getInputMethodSelectionKeyStroke(Preferences root) {
try {
if (root.nodeExists(inputMethodSelectionKeyPath)) {
Preferences node = root.node(inputMethodSelectionKeyPath);
int keyCode = node.getInt(inputMethodSelectionKeyCodeName, KeyEvent.VK_UNDEFINED);
if (keyCode != KeyEvent.VK_UNDEFINED) {
int modifiers = node.getInt(inputMethodSelectionKeyModifiersName, 0);
return AWTKeyStroke.getAWTKeyStroke(keyCode, modifiers);
}
}
} catch (BackingStoreException bse) {
}
return null;
}
}