/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Dmitry A. Durnev * @version $Revision$ */ package org.apache.harmony.awt.im; //???AWT import java.awt.AWTEvent; import java.awt.Component; //import java.awt.KeyboardFocusManager; import java.awt.Rectangle; //import java.awt.Window; import java.awt.event.FocusEvent; import java.awt.event.InputMethodEvent; import java.awt.event.KeyEvent; import java.awt.font.TextHitInfo; import java.awt.im.InputContext; import java.awt.im.InputMethodRequests; import java.awt.im.spi.InputMethod; import java.awt.im.spi.InputMethodDescriptor; import java.lang.Character.Subset; import java.text.AttributedCharacterIterator; import java.text.AttributedCharacterIterator.Attribute; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Set; //???AWT //import javax.swing.JFrame; import org.apache.harmony.awt.wtk.NativeIM; /** * Implementation of InputMethodContext * interface, also provides all useful * functionality of InputContext * */ public class InputMethodContext extends InputContext implements java.awt.im.spi.InputMethodContext { //???AWT private InputMethod inputMethod; // current IM private Component client; // current "active" client component //???AWT: private CompositionWindow composeWindow; // composition Window private final Map<InputMethodDescriptor, InputMethod> imInstances; // Map<InputMethodDescriptor, InputMethod> private final Map<Locale, InputMethod> localeIM; // Map<Locale, InputMethod> last user-selected IM for locale private final Set<InputMethod> notifyIM; // set of IMs to notify of client window bounds changes /** * a flag indicating that IM should be notified of client window * position/visibility changes as soon as it is activated(new client * appears) */ private boolean pendingClientNotify; private Component nextComp; // component to gain focus after endComposition() //???AWT: private final Set<Window> imWindows; // set of all IM windows created by this instance private final NativeIM nativeIM; public InputMethodContext() { notifyIM = new HashSet<InputMethod>(); //???AWT: imWindows = new HashSet<Window>(); imInstances = new HashMap<InputMethodDescriptor, InputMethod>(); localeIM = new HashMap<Locale, InputMethod>(); selectInputMethod(Locale.US); // not default? nativeIM = (NativeIM) inputMethod; } //???AWT /* @Override public void dispatchEvent(AWTEvent event) { int id = event.getID(); if ((id >= FocusEvent.FOCUS_FIRST) && (id <=FocusEvent.FOCUS_LAST)) { dispatchFocusEvent((FocusEvent) event); } else { // handle special KEY_PRESSED // event to show IM selection menu if (id == KeyEvent.KEY_PRESSED) { KeyEvent ke = (KeyEvent) event; IMManager.selectIM(ke, this, IMManager.getWindow(ke.getComponent())); } // dispatch all input events to the current IM: if (inputMethod != null) { inputMethod.dispatchEvent(event); } } } private void dispatchFocusEvent(FocusEvent fe) { switch (fe.getID()) { case FocusEvent.FOCUS_LOST: if (inputMethod != null) { inputMethod.deactivate(fe.isTemporary()); } break; case FocusEvent.FOCUS_GAINED: Component comp = fe.getComponent(); if (imWindows.contains(comp)) { // prevent activating when IM windows // attached to this context gain focus return; } InputMethodContext lastActive = IMManager.getLastActiveIMC(); if ((lastActive != this) && (lastActive != null)) { lastActive.hideWindows(); } if (inputMethod != null) { activateIM(inputMethod); if (!getCompositionWindow().isEmpty()) { IMManager.showCompositionWindow(composeWindow); } if (client == comp) { if (nextComp != null) { // temporarily got focus to // end composition endComposition(); // transfer focus to new client client = nextComp; nextComp = null; client.requestFocusInWindow(); } } else if ((client != null) && getCompositionWindow().isVisible()) { // temporarily return focus back // to previous client to be able // to end composition nextComp = comp; client.requestFocusInWindow(); } else { client = comp; } } if (pendingClientNotify) { notifyClientWindowChange(IMManager.getWindow(comp).getBounds()); } break; } } private void activateIM(InputMethod im) { im.activate(); if ((nativeIM != null) && (im != nativeIM)) { // when Java IM is active // native input method editor must be // explicitly disabled nativeIM.disableIME(); } IMManager.setLastActiveIMC(this); } @SuppressWarnings("deprecation") private void hideWindows() { if (inputMethod != null) { inputMethod.hideWindows(); } if (composeWindow != null) { composeWindow.hide(); } } private void createCompositionWindow() { composeWindow = new CompositionWindow(client); } private CompositionWindow getCompositionWindow() { if (composeWindow == null) { createCompositionWindow(); } composeWindow.setClient(client); return composeWindow; } */ /** * Gets input method requests for the current client * irrespective of input style. * @return input method requests of composition window if * client is passive, * otherwise input method requests of client */ private InputMethodRequests getIMRequests() { InputMethodRequests imRequests = null; if (client != null) { imRequests = client.getInputMethodRequests(); //???AWT /* if (imRequests == null) { imRequests = getCompositionWindow().getInputMethodRequests(); } */ } return imRequests; } /** * Gets input method requests for the current client & input style. * @return input method requests of composition window if * input style is "below-the-spot"(or client is passive), * otherwise client input method requests */ private InputMethodRequests getStyleIMRequests() { //???AWT /* if (IMManager.belowTheSpot()) { return getCompositionWindow().getInputMethodRequests(); } */ return getIMRequests(); } @Override public void dispose() { if (inputMethod != null) { closeIM(inputMethod); inputMethod.dispose(); } notifyIM.clear(); super.dispose(); } @Override public void endComposition() { if (inputMethod != null) { inputMethod.endComposition(); } super.endComposition(); } @Override public Object getInputMethodControlObject() { if (inputMethod != null) { return inputMethod.getControlObject(); } return super.getInputMethodControlObject(); } @Override public Locale getLocale() { if (inputMethod != null) { return inputMethod.getLocale(); } return super.getLocale(); } @Override public boolean isCompositionEnabled() { if (inputMethod != null) { return inputMethod.isCompositionEnabled(); } return super.isCompositionEnabled(); } @Override public void reconvert() { if (inputMethod != null) { inputMethod.reconvert(); } super.reconvert(); } //???AWT /* @Override public void removeNotify(Component client) { if ((inputMethod != null) && (client == this.client)) { inputMethod.removeNotify(); client = null; // set flag indicating that IM should be notified // as soon as it is activated(new client appears) pendingClientNotify = true; } super.removeNotify(client); } */ @Override public boolean selectInputMethod(Locale locale) { if ((inputMethod != null) && inputMethod.setLocale(locale)) { return true; } // first // take last user-selected IM for locale InputMethod newIM = localeIM.get(locale); // if not found search through IM descriptors // and take already created instance if exists // or create, store new IM instance in descriptor->instance map //???AWT /* if (newIM == null) { try { newIM = getIMInstance(IMManager.getIMDescriptors().iterator(), locale); } catch (Exception e) { // ignore exceptions - just return false } } */ return switchToIM(locale, newIM); } private boolean switchToIM(Locale locale, InputMethod newIM) { //???AWT /* if (newIM != null) { closeIM(inputMethod); client = KeyboardFocusManager. getCurrentKeyboardFocusManager().getFocusOwner(); initIM(newIM, locale); inputMethod = newIM; return true; } */ return false; } /** * Is called when IM is selected from UI */ void selectIM(InputMethodDescriptor imd, Locale locale) { try { switchToIM(locale, getIMInstance(imd)); } catch (Exception e) { e.printStackTrace(); } } /** * Gets input method instance for the given * locale from the given list of descriptors * @param descriptors iterator of the list of IM descriptors * @param locale the locale to be supported by the IM * @return input method instance * @throws Exception */ private InputMethod getIMInstance(Iterator<InputMethodDescriptor> descriptors, Locale locale) throws Exception { while (descriptors.hasNext()) { InputMethodDescriptor desc = descriptors.next(); Locale[] locs = desc.getAvailableLocales(); for (Locale element : locs) { if (locale.equals(element)) { return getIMInstance(desc); } } } return null; } private InputMethod getIMInstance(InputMethodDescriptor imd) throws Exception { InputMethod im = imInstances.get(imd); if (im == null) { im = imd.createInputMethod(); im.setInputMethodContext(this); imInstances.put(imd, im); } return im; } private void initIM(InputMethod im, Locale locale) { if (im == null) { return; } im.setLocale(locale); im.setCharacterSubsets(null); //???AWT: activateIM(im); try { im.setCompositionEnabled(inputMethod != null ? inputMethod.isCompositionEnabled() : true); } catch (UnsupportedOperationException uoe) { } } private void closeIM(InputMethod im) { if (im == null) { return; } if (im.isCompositionEnabled()) { im.endComposition(); } im.deactivate(true); im.hideWindows(); } @Override public void setCharacterSubsets(Subset[] subsets) { if (inputMethod != null) { inputMethod.setCharacterSubsets(subsets); } super.setCharacterSubsets(subsets); } @Override public void setCompositionEnabled(boolean enable) { if (inputMethod != null) { inputMethod.setCompositionEnabled(enable); } super.setCompositionEnabled(enable); } //???AWT /* public JFrame createInputMethodJFrame(String title, boolean attachToInputContext) { JFrame jf = new IMJFrame(title, attachToInputContext ? this : null); imWindows.add(jf); return jf; } public Window createInputMethodWindow(String title, boolean attachToInputContext) { Window w = new IMWindow(title, attachToInputContext ? this : null); imWindows.add(w); return w; } */ @SuppressWarnings("deprecation") public void dispatchInputMethodEvent(int id, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition) { if (client == null) { return; } //???AWT /* InputMethodEvent ime = new InputMethodEvent(client, id, text, committedCharacterCount, caret, visiblePosition); if ((client.getInputMethodRequests() != null) && !IMManager.belowTheSpot()) { client.dispatchEvent(ime); } else { // show/hide composition window if necessary if (committedCharacterCount < text.getEndIndex()) { IMManager.showCompositionWindow(getCompositionWindow()); } else { getCompositionWindow().hide(); } composeWindow.getActiveClient().dispatchEvent(ime); } */ } public void enableClientWindowNotification(InputMethod inputMethod, boolean enable) { if (enable) { notifyIM.add(inputMethod); //???AWT /* if (client != null) { notifyClientWindowChange(IMManager.getWindow(client).getBounds()); } else { pendingClientNotify = true; } */ } else { notifyIM.remove(inputMethod); } } public AttributedCharacterIterator cancelLatestCommittedText( Attribute[] attributes) { return getIMRequests().cancelLatestCommittedText(attributes); } public AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex, Attribute[] attributes) { return getIMRequests().getCommittedText(beginIndex, endIndex, attributes); } public int getCommittedTextLength() { return getIMRequests().getCommittedTextLength(); } public int getInsertPositionOffset() { return getIMRequests().getInsertPositionOffset(); } public TextHitInfo getLocationOffset(int x, int y) { InputMethodRequests imr = getStyleIMRequests(); if (imr != null) { return imr.getLocationOffset(x, y); } return null; } public AttributedCharacterIterator getSelectedText(Attribute[] attributes) { return getIMRequests().getSelectedText(attributes); } public Rectangle getTextLocation(TextHitInfo offset) { return getStyleIMRequests().getTextLocation(offset); } /** * To be called by AWT when client Window's bounds/visibility/state * change */ public void notifyClientWindowChange(Rectangle bounds) { if (notifyIM.contains(inputMethod)) { inputMethod.notifyClientWindowChange(bounds); } pendingClientNotify = false; } public final InputMethod getInputMethod() { return inputMethod; } public final Component getClient() { return client; } public final NativeIM getNativeIM() { return nativeIM; } }