/* * Copyright (c) 1997, 2012, 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 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 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. *

* 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.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); } } 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(); 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 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) { 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); PlatformLogger logger = PlatformLogger.getLogger("sun.awt.im"); 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() { 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; } }