/*
* Copyright (c) 1998, 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.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;
/**
* ExecutableInputMethodManager is the implementation of the
* InputMethodManager class. It is runnable as a separate
* thread in the AWT environment.
* InputMethodManager.getInstance() creates an instance of
* ExecutableInputMethodManager 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 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();
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