| /* InputContext.java -- provides the context for text input | |
| Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.awt.im; | |
| import gnu.java.util.EmptyEnumeration; | |
| import java.awt.AWTEvent; | |
| import java.awt.AWTException; | |
| import java.awt.Component; | |
| import java.awt.im.spi.InputMethod; | |
| import java.awt.im.spi.InputMethodDescriptor; | |
| import java.io.BufferedReader; | |
| import java.io.IOException; | |
| import java.io.InputStreamReader; | |
| import java.net.URL; | |
| import java.text.AttributedCharacterIterator.Attribute; | |
| import java.util.ArrayList; | |
| import java.util.Enumeration; | |
| import java.util.HashMap; | |
| import java.util.Locale; | |
| /** | |
| * Provides a context for controlling input methods and keyboard layouts. | |
| * This class provides the communication layer between the client component, | |
| * and the various locale-dependent text entry input methods that can be used | |
| * for the client. By default, there is one instance per Window, shared among | |
| * all components, but this limits text entry to one component at a time. | |
| * Thus, text components can create their own instance to allow text entry | |
| * in multiple components at a time. | |
| * | |
| * <p>By using the interfaces of {@link java.awt.im.spi}, you can install | |
| * extensions which allow additional input methods. Some of these may use | |
| * platform native input methods, or keyboard layouts provided by the platform. | |
| * Input methods are unavailable if none have been installed and the platform | |
| * has no underlying native input methods. Extensions are installed as jar | |
| * files, usually accessed in the default extension location or specified by | |
| * the -extdir VM flag. The jar must contain a file named | |
| * "META_INF/services/java.awt.im.spi.InputMethodDescriptor" which lists, | |
| * one entry per line in UTF-8 encoding, each class in the jar that implements | |
| * java.awt.im.spi.InputMethodDescriptor. | |
| * | |
| * @author Eric Blake (ebb9@email.byu.edu) | |
| * @see Component#getInputContext() | |
| * @see Component#enableInputMethods(boolean) | |
| * @since 1.2 | |
| * @status updated to 1.4, but unverified | |
| */ | |
| public class InputContext | |
| { | |
| /** | |
| * The list of installed input method descriptors. | |
| */ | |
| private static final ArrayList descriptors = new ArrayList(); | |
| static | |
| { | |
| Enumeration e; | |
| try | |
| { | |
| e = ClassLoader.getSystemResources | |
| ("META_INF/services/java.awt.im.spi.InputMethodDescriptor"); | |
| } | |
| catch (IOException ex) | |
| { | |
| // XXX Should we do something else? | |
| e = EmptyEnumeration.getInstance(); | |
| } | |
| while (e.hasMoreElements()) | |
| { | |
| URL url = (URL) e.nextElement(); | |
| BufferedReader in; | |
| String line; | |
| try | |
| { | |
| in = new BufferedReader | |
| (new InputStreamReader(url.openConnection().getInputStream(), | |
| "UTF-8")); | |
| line = in.readLine().trim(); | |
| } | |
| catch (IOException ignored) | |
| { | |
| continue; | |
| } | |
| outer: | |
| while (line != null) | |
| { | |
| try | |
| { | |
| if (line.charAt(0) != '#') | |
| { | |
| Class c = Class.forName(line); | |
| descriptors.add((InputMethodDescriptor) c.newInstance()); | |
| } | |
| line = in.readLine().trim(); | |
| } | |
| catch (IOException ex) | |
| { | |
| continue outer; | |
| } | |
| catch (Exception ignored) | |
| { | |
| } | |
| } | |
| } | |
| } | |
| /** The current input method; null if no input methods are installed. */ | |
| private InputMethod im; | |
| /** Map of locales to the most recently selected input method. */ | |
| private final HashMap recent = new HashMap(); | |
| /** The list of acceptable character subsets. */ | |
| private Character.Subset[] subsets; | |
| /** | |
| * Construct an InputContext. This is protected, so clients must use | |
| * {@link #getInstance()} instead. | |
| */ | |
| protected InputContext() | |
| { | |
| } | |
| /** | |
| * Returns a new InputContext. | |
| * | |
| * @return a new instance, initialized to the default locale if available | |
| */ | |
| public static InputContext getInstance() | |
| { | |
| InputContext ic = new InputContext(); | |
| ic.selectInputMethod(Locale.getDefault()); | |
| return ic; | |
| } | |
| /** | |
| * Attempts to select an input method or keyboard layout which supports the | |
| * given locale. This returns true if a locale is available and was selected. | |
| * The following steps are taken in choosing an input method:<ul> | |
| * <li>If the currently selected input method or keyboard layout supports | |
| * the requested locale, it remains selected.</li> | |
| * <li>If there is no input method or keyboard layout available that | |
| * supports the requested locale, the current input method or keyboard | |
| * layout remains selected.</li> | |
| * <li>If the user has previously selected an input method or keyboard | |
| * layout for the requested locale from the user interface, then the most | |
| * recently selected such input method or keyboard layout is reselected.</li> | |
| * <li>Otherwise, an input method or keyboard layout that supports the | |
| * requested locale is selected in an implementation dependent way. This | |
| * implementation chooses the first input method which supports the requested | |
| * locale based on the InputMethodDescriptors loaded from the extensions | |
| * installed on the CLASSPATH.</li> | |
| * </ul> | |
| * | |
| * <p>Before switching away from an input method, any currently uncommitted | |
| * text is committed. Not all host operating systems provide API to | |
| * determine the locale of the currently selected native input method or | |
| * keyboard layout, and to select a native input method or keyboard layout | |
| * by locale. For host operating systems that don't provide such API, | |
| * selectInputMethod assumes that native input methods or keyboard layouts | |
| * provided by the host operating system support only the system's default | |
| * locale. | |
| * | |
| * <p>An example of where this may be called is in a multi-language document, | |
| * when moving the insertion point between sections of different locale, so | |
| * that the user may use the input method appropriate to that section of the | |
| * document. | |
| * | |
| * @param locale the desired new locale | |
| * @return true if the new locale is active | |
| * @throws NullPointerException if locale is null | |
| */ | |
| public boolean selectInputMethod(Locale locale) | |
| { | |
| if (im != null && im.setLocale(locale)) | |
| { | |
| recent.put(locale, im); | |
| return true; | |
| } | |
| InputMethod next = (InputMethod) recent.get(locale); | |
| outer: | |
| if (next != null) | |
| for (int i = 0, limit = descriptors.size(); i < limit; i++) | |
| { | |
| InputMethodDescriptor d = (InputMethodDescriptor) descriptors.get(i); | |
| Locale[] list; | |
| try | |
| { | |
| list = d.getAvailableLocales(); | |
| } | |
| catch (AWTException ignored) | |
| { | |
| continue; | |
| } | |
| for (int j = list.length; --j >= 0; ) | |
| if (locale.equals(list[j])) | |
| { | |
| try | |
| { | |
| next = d.createInputMethod(); | |
| recent.put(locale, next); | |
| } | |
| catch (Exception ignored) | |
| { | |
| continue; | |
| } | |
| } | |
| } | |
| if (next == null) | |
| return false; | |
| // XXX I'm not sure if this does all the necessary steps in the switch. | |
| if (im != null) | |
| { | |
| try | |
| { | |
| next.setCompositionEnabled(im.isCompositionEnabled()); | |
| } | |
| catch (UnsupportedOperationException ignored) | |
| { | |
| } | |
| im.endComposition(); | |
| im.deactivate(false); | |
| im.hideWindows(); | |
| } | |
| im = next; | |
| im.setLocale(locale); | |
| im.setCharacterSubsets(subsets); | |
| return true; | |
| } | |
| /** | |
| * Returns the current locale of the current input method or keyboard | |
| * layout. Returns null if the input context does not have a current input | |
| * method or keyboard layout or if the current input method's | |
| * {@link InputMethod#getLocale()} method returns null. Not all host | |
| * operating systems provide API to determine the locale of the currently | |
| * selected native input method or keyboard layout. For host operating | |
| * systems that don't provide such API, getLocale assumes that the current | |
| * locale of all native input methods or keyboard layouts provided by the | |
| * host operating system is the system's default locale. | |
| * | |
| * @return the locale of the current input method, or null | |
| * @since 1.3 | |
| */ | |
| public Locale getLocale() | |
| { | |
| return im == null ? null : im.getLocale(); | |
| } | |
| /** | |
| * Sets the subsets of Unicode characters allowed to be input by the current | |
| * input method, as well as subsequent input methods. The value of null | |
| * implies all characters are legal. Applications should not rely on this | |
| * behavior, since native host input methods may not allow restrictions. | |
| * If no current input method is available, this has no immediate effect. | |
| * | |
| * @param subsets the set of Unicode subsets to accept, or null | |
| */ | |
| public void setCharacterSubsets(Character.Subset[] subsets) | |
| { | |
| this.subsets = subsets; | |
| if (im != null) | |
| im.setCharacterSubsets(subsets); | |
| } | |
| /** | |
| * Changes the enabled status of the current input method. An input method | |
| * that is enabled for composition interprets incoming events for both | |
| * composition and control purposes, while a disabled input method only | |
| * interprets control commands (including commands to enable itself). | |
| * | |
| * @param enable whether to enable the input method | |
| * @throws UnsupportedOperationException if there is no current input method, | |
| * or the input method does not support enabling | |
| * @see #isCompositionEnabled() | |
| * @since 1.3 | |
| */ | |
| public void setCompositionEnabled(boolean enable) | |
| { | |
| if (im == null) | |
| throw new UnsupportedOperationException(); | |
| im.setCompositionEnabled(enable); | |
| } | |
| /** | |
| * Find out if the current input method is enabled. | |
| * | |
| * @return true if the current input method is enabled | |
| * @throws UnsupportedOperationException if there is no current input method, | |
| * or the input method does not support enabling | |
| * @see #setCompositionEnabled(boolean) | |
| * @since 1.3 | |
| */ | |
| public boolean isCompositionEnabled() | |
| { | |
| if (im == null) | |
| throw new UnsupportedOperationException(); | |
| return im.isCompositionEnabled(); | |
| } | |
| /** | |
| * Starts a reconversion operation in the current input method. The input | |
| * method gets the text to reconvert from the client component, using | |
| * {@link InputMethodRequests#getSelectedText(Attribute[])}. Then the | |
| * composed and committed text produced by the operation is sent back to | |
| * the client using a sequence of InputMethodRequests. | |
| * | |
| * @throws UnsupportedOperationException if there is no current input method, | |
| * or the input method does not support reconversion | |
| * @since 1.3 | |
| */ | |
| public void reconvert() | |
| { | |
| if (im == null) | |
| throw new UnsupportedOperationException(); | |
| im.reconvert(); | |
| } | |
| /** | |
| * Dispatches an event to the current input method. This is called | |
| * automatically by AWT. If no input method is available, then the event | |
| * will never be consumed. | |
| * | |
| * @param event the event to dispatch | |
| * @throws NullPointerException if event is null | |
| */ | |
| public void dispatchEvent(AWTEvent event) | |
| { | |
| if (im != null) | |
| im.dispatchEvent(event); | |
| } | |
| /** | |
| * Notifies the input context that a client component has been removed from | |
| * its containment hierarchy, or that input method support has been disabled | |
| * for the component. This method is usually called from the client | |
| * component's {@link Component#removeNotify()} method. Potentially pending | |
| * input from input methods for this component is discarded. If no input | |
| * methods are available, then this method has no effect. | |
| * | |
| * @param client the client component | |
| * @throws NullPointerException if client is null | |
| */ | |
| public void removeNotify(Component client) | |
| { | |
| // XXX What to do with client information? | |
| if (im != null) | |
| { | |
| im.deactivate(false); | |
| im.removeNotify(); | |
| } | |
| } | |
| /** | |
| * Ends any input composition that may currently be going on in this | |
| * context. Depending on the platform and possibly user preferences, this | |
| * may commit or delete uncommitted text. Any changes to the text are | |
| * communicated to the active component using an input method event. If no | |
| * input methods are available, then this method has no effect. This may | |
| * be called for a variety of reasons, such as when the user moves the | |
| * insertion point in the client text outside the range of the composed text, | |
| * or when text is saved to file. | |
| */ | |
| public void endComposition() | |
| { | |
| if (im != null) | |
| im.endComposition(); | |
| } | |
| /** | |
| * Disposes of the input context and release the resources used by it. | |
| * Called automatically by AWT for the default input context of each | |
| * Window. If no input methods are available, then this method has no | |
| * effect. | |
| */ | |
| public void dispose() | |
| { | |
| if (im != null) | |
| { | |
| im.deactivate(false); | |
| im.dispose(); | |
| } | |
| } | |
| /** | |
| * Returns a control object from the current input method, or null. A | |
| * control object provides implementation-dependent methods that control | |
| * the behavior of the input method or obtain information from the input | |
| * method. Clients have to compare the result against known input method | |
| * control object types. If no input methods are available or the current | |
| * input method does not provide an input method control object, then null | |
| * is returned. | |
| * | |
| * @return the control object, or null | |
| */ | |
| public Object getInputMethodControlObject() | |
| { | |
| return im == null ? null : im.getControlObject(); | |
| } | |
| } // class InputContext |