0N/A/*
2362N/A * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.awt.im;
0N/A
0N/Aimport java.awt.AWTEvent;
0N/Aimport java.awt.Component;
0N/Aimport java.awt.GraphicsEnvironment;
0N/Aimport java.awt.HeadlessException;
0N/Aimport java.awt.Rectangle;
0N/Aimport java.awt.Toolkit;
0N/Aimport java.awt.Window;
0N/Aimport java.awt.event.KeyEvent;
0N/Aimport java.awt.event.InputMethodEvent;
0N/Aimport java.awt.font.TextHitInfo;
0N/Aimport java.awt.im.InputMethodRequests;
0N/Aimport java.awt.im.spi.InputMethod;
0N/Aimport java.security.AccessController;
0N/Aimport java.text.AttributedCharacterIterator;
0N/Aimport java.text.AttributedCharacterIterator.Attribute;
0N/Aimport java.text.AttributedString;
0N/Aimport java.text.CharacterIterator;
0N/Aimport javax.swing.JFrame;
0N/Aimport sun.awt.InputMethodSupport;
0N/Aimport sun.security.action.GetPropertyAction;
0N/A
0N/A/**
0N/A * The InputMethodContext class provides methods that input methods
0N/A * can use to communicate with their client components.
0N/A * It is a subclass of InputContext, which provides methods for use by
0N/A * components.
0N/A *
0N/A * @author JavaSoft International
0N/A */
0N/A
0N/Apublic class InputMethodContext
0N/A extends sun.awt.im.InputContext
0N/A implements java.awt.im.spi.InputMethodContext {
0N/A
0N/A private boolean dispatchingCommittedText;
0N/A
0N/A // Creation of the context's composition area handler is
0N/A // delayed until we really need a composition area.
0N/A private CompositionAreaHandler compositionAreaHandler;
0N/A private Object compositionAreaHandlerLock = new Object();
0N/A
0N/A static private boolean belowTheSpotInputRequested;
0N/A private boolean inputMethodSupportsBelowTheSpot;
0N/A
0N/A static {
0N/A // check whether we should use below-the-spot input
0N/A // get property from command line
0N/A String inputStyle = (String) AccessController.doPrivileged
0N/A (new GetPropertyAction("java.awt.im.style", null));
0N/A // get property from awt.properties file
0N/A if (inputStyle == null) {
0N/A inputStyle = Toolkit.getDefaultToolkit().
0N/A getProperty("java.awt.im.style", null);
0N/A }
0N/A belowTheSpotInputRequested = "below-the-spot".equals(inputStyle);
0N/A }
0N/A
0N/A /**
0N/A * Constructs an InputMethodContext.
0N/A */
0N/A public InputMethodContext() {
0N/A super();
0N/A }
0N/A
0N/A void setInputMethodSupportsBelowTheSpot(boolean supported) {
0N/A inputMethodSupportsBelowTheSpot = supported;
0N/A }
0N/A
0N/A boolean useBelowTheSpotInput() {
0N/A return belowTheSpotInputRequested && inputMethodSupportsBelowTheSpot;
0N/A }
0N/A
0N/A private boolean haveActiveClient() {
0N/A Component client = getClientComponent();
0N/A return client != null
0N/A && client.getInputMethodRequests() != null;
0N/A }
0N/A
0N/A // implements java.awt.im.spi.InputMethodContext.dispatchInputMethodEvent
0N/A public void dispatchInputMethodEvent(int id,
0N/A AttributedCharacterIterator text, int committedCharacterCount,
0N/A TextHitInfo caret, TextHitInfo visiblePosition) {
0N/A // We need to record the client component as the source so
0N/A // that we have correct information if we later have to break up this
0N/A // event into key events.
0N/A Component source;
0N/A
0N/A source = getClientComponent();
0N/A if (source != null) {
0N/A InputMethodEvent event = new InputMethodEvent(source,
0N/A id, text, committedCharacterCount, caret, visiblePosition);
0N/A
0N/A if (haveActiveClient() && !useBelowTheSpotInput()) {
0N/A source.dispatchEvent(event);
0N/A } else {
0N/A getCompositionAreaHandler(true).processInputMethodEvent(event);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Dispatches committed text to a client component.
0N/A * Called by composition window.
0N/A *
0N/A * @param client The component that the text should get dispatched to.
0N/A * @param text The iterator providing access to the committed
0N/A * (and possible composed) text.
0N/A * @param committedCharacterCount The number of committed characters in the text.
0N/A */
0N/A synchronized void dispatchCommittedText(Component client,
0N/A AttributedCharacterIterator text,
0N/A int committedCharacterCount) {
0N/A // note that the client is not always the current client component -
0N/A // some host input method adapters may dispatch input method events
0N/A // through the Java event queue, and we may have switched clients while
0N/A // the event was in the queue.
0N/A if (committedCharacterCount == 0
0N/A || text.getEndIndex() <= text.getBeginIndex()) {
0N/A return;
0N/A }
0N/A long time = System.currentTimeMillis();
0N/A dispatchingCommittedText = true;
0N/A try {
0N/A InputMethodRequests req = client.getInputMethodRequests();
0N/A if (req != null) {
0N/A // active client -> send text as InputMethodEvent
0N/A int beginIndex = text.getBeginIndex();
0N/A AttributedCharacterIterator toBeCommitted =
0N/A (new AttributedString(text, beginIndex, beginIndex + committedCharacterCount)).getIterator();
0N/A
0N/A InputMethodEvent inputEvent = new InputMethodEvent(
0N/A client,
0N/A InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
0N/A toBeCommitted,
0N/A committedCharacterCount,
0N/A null, null);
0N/A
0N/A client.dispatchEvent(inputEvent);
0N/A } else {
0N/A // passive client -> send text as KeyEvents
0N/A char keyChar = text.first();
0N/A while (committedCharacterCount-- > 0 && keyChar != CharacterIterator.DONE) {
0N/A KeyEvent keyEvent = new KeyEvent(client, KeyEvent.KEY_TYPED,
0N/A time, 0, KeyEvent.VK_UNDEFINED, keyChar);
0N/A client.dispatchEvent(keyEvent);
0N/A keyChar = text.next();
0N/A }
0N/A }
0N/A } finally {
0N/A dispatchingCommittedText = false;
0N/A }
0N/A }
0N/A
0N/A public void dispatchEvent(AWTEvent event) {
0N/A // some host input method adapters may dispatch input method events
0N/A // through the Java event queue. If the component that the event is
0N/A // intended for isn't an active client, or if we're using below-the-spot
0N/A // input, we need to dispatch this event
0N/A // to the input window. Note that that component is not necessarily the
0N/A // current client component, since we may have switched clients while
0N/A // the event was in the queue.
0N/A if (event instanceof InputMethodEvent) {
0N/A if (((Component) event.getSource()).getInputMethodRequests() == null
0N/A || (useBelowTheSpotInput() && !dispatchingCommittedText)) {
0N/A getCompositionAreaHandler(true).processInputMethodEvent((InputMethodEvent) event);
0N/A }
0N/A } else {
0N/A // make sure we don't dispatch our own key events back to the input method
0N/A if (!dispatchingCommittedText) {
0N/A super.dispatchEvent(event);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Gets this context's composition area handler, creating it if necessary.
0N/A * If requested, it grabs the composition area for use by this context.
0N/A * The composition area's text is not updated.
0N/A */
0N/A private CompositionAreaHandler getCompositionAreaHandler(boolean grab) {
0N/A synchronized(compositionAreaHandlerLock) {
0N/A if (compositionAreaHandler == null) {
0N/A compositionAreaHandler = new CompositionAreaHandler(this);
0N/A }
0N/A compositionAreaHandler.setClientComponent(getClientComponent());
0N/A if (grab) {
0N/A compositionAreaHandler.grabCompositionArea(false);
0N/A }
0N/A
0N/A return compositionAreaHandler;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Grabs the composition area for use by this context.
0N/A * If doUpdate is true, updates the composition area with previously sent
0N/A * composed text.
0N/A */
0N/A void grabCompositionArea(boolean doUpdate) {
0N/A synchronized(compositionAreaHandlerLock) {
0N/A if (compositionAreaHandler != null) {
0N/A compositionAreaHandler.grabCompositionArea(doUpdate);
0N/A } else {
0N/A // if this context hasn't seen a need for a composition area yet,
0N/A // just close it without creating the machinery
0N/A CompositionAreaHandler.closeCompositionArea();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Releases and closes the composition area if it is currently owned by
0N/A * this context's composition area handler.
0N/A */
0N/A void releaseCompositionArea() {
0N/A synchronized(compositionAreaHandlerLock) {
0N/A if (compositionAreaHandler != null) {
0N/A compositionAreaHandler.releaseCompositionArea();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Calls CompositionAreaHandler.isCompositionAreaVisible() to see
0N/A * whether the composition area is visible or not.
0N/A * Notice that this method is always called on the AWT event dispatch
0N/A * thread.
0N/A */
0N/A boolean isCompositionAreaVisible() {
0N/A if (compositionAreaHandler != null) {
0N/A return compositionAreaHandler.isCompositionAreaVisible();
0N/A }
0N/A
0N/A return false;
0N/A }
0N/A /**
0N/A * Calls CompositionAreaHandler.setCompositionAreaVisible to
0N/A * show or hide the composition area.
0N/A * As isCompositionAreaVisible method, it is always called
0N/A * on AWT event dispatch thread.
0N/A */
0N/A void setCompositionAreaVisible(boolean visible) {
0N/A if (compositionAreaHandler != null) {
0N/A compositionAreaHandler.setCompositionAreaVisible(visible);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Calls the current client component's implementation of getTextLocation.
0N/A */
0N/A public Rectangle getTextLocation(TextHitInfo offset) {
0N/A return getReq().getTextLocation(offset);
0N/A }
0N/A
0N/A /**
0N/A * Calls the current client component's implementation of getLocationOffset.
0N/A */
0N/A public TextHitInfo getLocationOffset(int x, int y) {
0N/A return getReq().getLocationOffset(x, y);
0N/A }
0N/A
0N/A /**
0N/A * Calls the current client component's implementation of getInsertPositionOffset.
0N/A */
0N/A public int getInsertPositionOffset() {
0N/A return getReq().getInsertPositionOffset();
0N/A }
0N/A
0N/A /**
0N/A * Calls the current client component's implementation of getCommittedText.
0N/A */
0N/A public AttributedCharacterIterator getCommittedText(int beginIndex,
0N/A int endIndex,
0N/A Attribute[] attributes) {
0N/A return getReq().getCommittedText(beginIndex, endIndex, attributes);
0N/A }
0N/A
0N/A /**
0N/A * Calls the current client component's implementation of getCommittedTextLength.
0N/A */
0N/A public int getCommittedTextLength() {
0N/A return getReq().getCommittedTextLength();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Calls the current client component's implementation of cancelLatestCommittedText.
0N/A */
0N/A public AttributedCharacterIterator cancelLatestCommittedText(Attribute[] attributes) {
0N/A return getReq().cancelLatestCommittedText(attributes);
0N/A }
0N/A
0N/A /**
0N/A * Calls the current client component's implementation of getSelectedText.
0N/A */
0N/A public AttributedCharacterIterator getSelectedText(Attribute[] attributes) {
0N/A return getReq().getSelectedText(attributes);
0N/A }
0N/A
0N/A private InputMethodRequests getReq() {
0N/A if (haveActiveClient() && !useBelowTheSpotInput()) {
0N/A return getClientComponent().getInputMethodRequests();
0N/A } else {
0N/A return getCompositionAreaHandler(false);
0N/A }
0N/A }
0N/A
0N/A // implements java.awt.im.spi.InputMethodContext.createInputMethodWindow
0N/A public Window createInputMethodWindow(String title, boolean attachToInputContext) {
0N/A InputContext context = attachToInputContext ? this : null;
0N/A return createInputMethodWindow(title, context, false);
0N/A }
0N/A
0N/A // implements java.awt.im.spi.InputMethodContext.createInputMethodJFrame
0N/A public JFrame createInputMethodJFrame(String title, boolean attachToInputContext) {
0N/A InputContext context = attachToInputContext ? this : null;
0N/A return (JFrame)createInputMethodWindow(title, context, true);
0N/A }
0N/A
0N/A static Window createInputMethodWindow(String title, InputContext context, boolean isSwing) {
0N/A if (GraphicsEnvironment.isHeadless()) {
0N/A throw new HeadlessException();
0N/A }
0N/A if (isSwing) {
0N/A return new InputMethodJFrame(title, context);
0N/A } else {
0N/A Toolkit toolkit = Toolkit.getDefaultToolkit();
0N/A if (toolkit instanceof InputMethodSupport) {
0N/A return ((InputMethodSupport)toolkit).createInputMethodWindow(
0N/A title, context);
0N/A }
0N/A }
0N/A throw new InternalError("Input methods must be supported");
0N/A }
0N/A
0N/A /**
0N/A * @see java.awt.im.spi.InputMethodContext#enableClientWindowNotification
0N/A */
0N/A public void enableClientWindowNotification(InputMethod inputMethod, boolean enable) {
0N/A super.enableClientWindowNotification(inputMethod, enable);
0N/A }
0N/A
0N/A /**
0N/A * Disables or enables decorations for the composition window.
0N/A */
0N/A void setCompositionAreaUndecorated(boolean undecorated) {
0N/A if (compositionAreaHandler != null) {
0N/A compositionAreaHandler.setCompositionAreaUndecorated(undecorated);
0N/A }
0N/A }
0N/A}