806 lines · 28.7 KB · Java
RawDownload
/* * Copyright (c) 1998, 2003, 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.AWTException;import java.awt.CheckboxMenuItem;import java.awt.Component;import java.awt.Dialog;import java.awt.EventQueue;import java.awt.Frame;import java.awt.PopupMenu;import java.awt.Menu;import java.awt.MenuItem;import java.awt.Toolkit;import sun.awt.AppContext;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.InvocationEvent;import java.awt.im.spi.InputMethodDescriptor;import java.lang.reflect.InvocationTargetException;import java.security.AccessController;import java.security.PrivilegedAction;import java.security.PrivilegedActionException;import java.security.PrivilegedExceptionAction;import java.util.Hashtable;import java.util.Iterator;import java.util.Locale;import java.util.ServiceLoader;import java.util.Vector;import java.util.Set;import java.util.prefs.BackingStoreException;import java.util.prefs.Preferences;import sun.awt.InputMethodSupport;import sun.awt.SunToolkit; /** * <code>InputMethodManager</code> is an abstract class that manages the input * method environment of JVM. There is only one <code>InputMethodManager</code> * instance in JVM that is executed under a separate daemon thread. * <code>InputMethodManager</code> performs the following: * <UL> * <LI> * Keeps track of the current input context.</LI> * * <LI> * Provides a user interface to switch input methods and notifies the current * input context about changes made from the user interface.</LI> * </UL> * * The mechanism for supporting input method switch is as follows. (Note that * this may change in future releases.) * * <UL> * <LI> * One way is to use platform-dependent window manager's menu (known as the <I>Window * menu </I>in Motif and the <I>System menu</I> or <I>Control menu</I> in * Win32) on each window which is popped up by clicking the left top box of * a window (known as <I>Window menu button</I> in Motif and <I>System menu * button</I> in Win32). This happens to be common in both Motif and Win32.</LI> * * <LI> * When more than one input method descriptor can be found or the only input * method descriptor found supports multiple locales, a menu item * is added to the window (manager) menu. This item label is obtained invoking * <code>getTriggerMenuString()</code>. If null is returned by this method, it * means that there is only input method or none in the environment. Frame and Dialog * invoke this method.</LI> * * <LI> * This menu item means a trigger switch to the user to pop up a selection * menu.</LI> * * <LI> * When the menu item of the window (manager) menu has been selected by the * user, Frame/Dialog invokes <code>notifyChangeRequest()</code> to notify * <code>InputMethodManager</code> that the user wants to switch input methods.</LI> * * <LI> * <code>InputMethodManager</code> displays a pop-up menu to choose an input method.</LI> * * <LI> * <code>InputMethodManager</code> notifies the current <code>InputContext</code> of * the selected <code>InputMethod</code>.</LI> * </UL> * * <UL> * <LI> * The other way is to use user-defined hot key combination to show the pop-up menu to * choose an input method.  This is useful for the platforms which do not provide a * way to add a menu item in the window (manager) menu.</LI> * * <LI> * When the hot key combination is typed by the user, the component which has the input * focus invokes <code>notifyChangeRequestByHotKey()</code> to notify * <code>InputMethodManager</code> that the user wants to switch input methods.</LI> * * <LI> * This results in a popup menu and notification to the current input context, * as above.</LI> * </UL> * * @see java.awt.im.spi.InputMethod * @see sun.awt.im.InputContext * @see sun.awt.im.InputMethodAdapter * @author JavaSoft International */ public abstract class InputMethodManager {     /**     * InputMethodManager thread name     */    private static final String threadName = "AWT-InputMethodManager";     /**     * Object for global locking     */    private static final Object LOCK = new Object();     /**     * The InputMethodManager instance     */    private static InputMethodManager inputMethodManager;     /**     * Returns the instance of InputMethodManager. This method creates     * the instance that is unique in the Java VM if it has not been     * created yet.     *     * @return the InputMethodManager instance     */    public static final InputMethodManager getInstance() {        if (inputMethodManager != null) {            return inputMethodManager;        }        synchronized(LOCK) {            if (inputMethodManager == null) {                ExecutableInputMethodManager imm = new ExecutableInputMethodManager();                 // Initialize the input method manager and start a                // daemon thread if the user has multiple input methods                // to choose from. Otherwise, just keep the instance.                if (imm.hasMultipleInputMethods()) {                    imm.initialize();                    Thread immThread = new Thread(imm, threadName);                    immThread.setDaemon(true);                    immThread.setPriority(Thread.NORM_PRIORITY + 1);                    immThread.start();                }                inputMethodManager = imm;            }        }        return inputMethodManager;    }     /**     * Gets a string for the trigger menu item that should be added to     * the window manager menu. If no need to display the trigger menu     * item, null is returned.     */    public abstract String getTriggerMenuString();     /**     * Notifies InputMethodManager that input method change has been     * requested by the user. This notification triggers a popup menu     * for user selection.     *     * @param comp Component that has accepted the change     * request. This component has to be a Frame or Dialog.     */    public abstract void notifyChangeRequest(Component comp);     /**     * Notifies InputMethodManager that input method change has been     * requested by the user using the hot key combination. This     * notification triggers a popup menu for user selection.     *     * @param comp Component that has accepted the change     * request. This component has the input focus.     */    public abstract void notifyChangeRequestByHotKey(Component comp);     /**     * Sets the current input context so that it will be notified     * of input method changes initiated from the user interface.     * Set to real input context when activating; to null when     * deactivating.     */    abstract void setInputContext(InputContext inputContext);     /**     * Tries to find an input method locator for the given locale.     * Returns null if no available input method locator supports     * the locale.     */    abstract InputMethodLocator findInputMethod(Locale forLocale);     /**     * Gets the default keyboard locale of the underlying operating system.     */    abstract Locale getDefaultKeyboardLocale();     /**     * Returns whether multiple input methods are available or not     */    abstract boolean hasMultipleInputMethods(); } /** * <code>ExecutableInputMethodManager</code> is the implementation of the * <code>InputMethodManager</code> class. It is runnable as a separate * thread in the AWT environment.&nbsp; * <code>InputMethodManager.getInstance()</code> creates an instance of * <code>ExecutableInputMethodManager</code> and executes it as a deamon * thread. * * @see InputMethodManager */class ExecutableInputMethodManager extends InputMethodManager                                   implements Runnable{    // the input context that's informed about selections from the user interface    private InputContext currentInputContext;     // Menu item string for the trigger menu.    private String triggerMenuString;     // popup menu for selecting an input method    private InputMethodPopupMenu selectionMenu;    private static String selectInputMethodMenuTitle;     // locator and name of host adapter    private InputMethodLocator hostAdapterLocator;     // locators for Java input methods    private int javaInputMethodCount;         // number of Java input methods found    private Vector<InputMethodLocator> javaInputMethodLocatorList;     // component that is requesting input method switch    // must be Frame or Dialog    private Component requestComponent;     // input context that is requesting input method switch    private InputContext requestInputContext;     // IM preference stuff    private static final String preferredIMNode = "/sun/awt/im/preferredInputMethod";    private static final String descriptorKey = "descriptor";    private Hashtable preferredLocatorCache = new Hashtable();    private Preferences userRoot;     ExecutableInputMethodManager() {         // set up host adapter locator        Toolkit toolkit = Toolkit.getDefaultToolkit();        try {            if (toolkit instanceof InputMethodSupport) {                InputMethodDescriptor hostAdapterDescriptor =                    ((InputMethodSupport)toolkit)                    .getInputMethodAdapterDescriptor();                if (hostAdapterDescriptor != null) {                    hostAdapterLocator = new InputMethodLocator(hostAdapterDescriptor, null, null);                }            }        } catch (AWTException e) {            // if we can't get a descriptor, we'll just have to do without native input methods        }         javaInputMethodLocatorList = new Vector<InputMethodLocator>();        initializeInputMethodLocatorList();    }     synchronized void initialize() {        selectInputMethodMenuTitle = Toolkit.getProperty("AWT.InputMethodSelectionMenu", "Select Input Method");         triggerMenuString = selectInputMethodMenuTitle;    }     public void run() {        // If there are no multiple input methods to choose from, wait forever        while (!hasMultipleInputMethods()) {            try {                synchronized (this) {                    wait();                }            } catch (InterruptedException e) {            }        }         // Loop for processing input method change requests        while (true) {            waitForChangeRequest();            initializeInputMethodLocatorList();            try {                if (requestComponent != null) {                    showInputMethodMenuOnRequesterEDT(requestComponent);                } else {                    // show the popup menu within the event thread                    EventQueue.invokeAndWait(new Runnable() {                        public void run() {                            showInputMethodMenu();                        }                    });                }            } catch (InterruptedException ie) {            } catch (InvocationTargetException ite) {                // should we do anything under these exceptions?            }        }    }     // Shows Input Method Menu on the EDT of requester component    // to avoid side effects. See 6544309.    private void showInputMethodMenuOnRequesterEDT(Component requester)        throws InterruptedException, InvocationTargetException {         if (requester == null){            return;        }         class AWTInvocationLock {}        Object lock = new AWTInvocationLock();         InvocationEvent event =                new InvocationEvent(requester,                                    new Runnable() {                                        public void run() {                                            showInputMethodMenu();                                        }                                    },                                    lock,                                    true);         AppContext requesterAppContext = SunToolkit.targetToAppContext(requester);        synchronized (lock) {            SunToolkit.postEvent(requesterAppContext, event);            while (!event.isDispatched()) {                lock.wait();            }        }         Throwable eventThrowable = event.getThrowable();        if (eventThrowable != null) {            throw new InvocationTargetException(eventThrowable);        }    }     void setInputContext(InputContext inputContext) {        if (currentInputContext != null && inputContext != null) {            // don't throw this exception until 4237852 is fixed            // throw new IllegalStateException("Can't have two active InputContext at the same time");        }        currentInputContext = inputContext;    }     public synchronized void notifyChangeRequest(Component comp) {        if (!(comp instanceof Frame || comp instanceof Dialog))            return;         // if busy with the current request, ignore this request.        if (requestComponent != null)            return;         requestComponent = comp;        notify();    }     public synchronized void notifyChangeRequestByHotKey(Component comp) {        while (!(comp instanceof Frame || comp instanceof Dialog)) {            if (comp == null) {                // no Frame or Dialog found in containment hierarchy.                return;            }            comp = comp.getParent();        }         notifyChangeRequest(comp);    }     public String getTriggerMenuString() {        return triggerMenuString;    }     /*     * Returns true if the environment indicates there are multiple input methods     */    boolean hasMultipleInputMethods() {        return ((hostAdapterLocator != null) && (javaInputMethodCount > 0)                || (javaInputMethodCount > 1));    }     private synchronized void waitForChangeRequest() {        try {            while (requestComponent == null) {                wait();            }        } catch (InterruptedException e) {        }    }     /*     * initializes the input method locator list for all     * installed input method descriptors.     */    private void initializeInputMethodLocatorList() {        synchronized (javaInputMethodLocatorList) {            javaInputMethodLocatorList.clear();            try {                AccessController.doPrivileged(new PrivilegedExceptionAction() {                    public Object run() {                        for (InputMethodDescriptor descriptor :                            ServiceLoader.loadInstalled(InputMethodDescriptor.class)) {                            ClassLoader cl = descriptor.getClass().getClassLoader();                            javaInputMethodLocatorList.add(new InputMethodLocator(descriptor, cl, null));                        }                        return null;                    }                });            }  catch (PrivilegedActionException e) {                e.printStackTrace();            }            javaInputMethodCount = javaInputMethodLocatorList.size();        }         if (hasMultipleInputMethods()) {            // initialize preferences            if (userRoot == null) {                userRoot = getUserRoot();            }        } else {            // indicate to clients not to offer the menu            triggerMenuString = null;        }    }     private void showInputMethodMenu() {         if (!hasMultipleInputMethods()) {            requestComponent = null;            return;        }         // initialize pop-up menu        selectionMenu = InputMethodPopupMenu.getInstance(requestComponent, selectInputMethodMenuTitle);         // we have to rebuild the menu each time because        // some input methods (such as IIIMP) may change        // their list of supported locales dynamically        selectionMenu.removeAll();         // get information about the currently selected input method        // ??? if there's no current input context, what's the point        // of showing the menu?        String currentSelection = getCurrentSelection();         // Add menu item for host adapter        if (hostAdapterLocator != null) {            selectionMenu.addOneInputMethodToMenu(hostAdapterLocator, currentSelection);            selectionMenu.addSeparator();        }         // Add menu items for other input methods        for (int i = 0; i < javaInputMethodLocatorList.size(); i++) {            InputMethodLocator locator = javaInputMethodLocatorList.get(i);            selectionMenu.addOneInputMethodToMenu(locator, currentSelection);        }         synchronized (this) {            selectionMenu.addToComponent(requestComponent);            requestInputContext = currentInputContext;            selectionMenu.show(requestComponent, 60, 80); // TODO: get proper x, y...            requestComponent = null;        }    }     private String getCurrentSelection() {

Lines 1–500 of 806.