|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class ExecutableInputMethodManager extends InputMethodManager |
|
implements Runnable |
|
{ |
|
|
|
private InputContext currentInputContext; |
|
|
|
|
|
private String triggerMenuString; |
|
|
|
|
|
private InputMethodPopupMenu selectionMenu; |
|
private static String selectInputMethodMenuTitle; |
|
|
|
|
|
private InputMethodLocator hostAdapterLocator; |
|
|
|
// locators for Java input methods |
|
private int javaInputMethodCount; |
|
private Vector<InputMethodLocator> javaInputMethodLocatorList; |
|
|
|
// component that is requesting input method switch |
|
|
|
private Component requestComponent; |
|
|
|
|
|
private InputContext requestInputContext; |
|
|
|
|
|
private static final String preferredIMNode = "/sun/awt/im/preferredInputMethod"; |
|
private static final String descriptorKey = "descriptor"; |
|
private Hashtable<String, InputMethodLocator> preferredLocatorCache = new Hashtable<>(); |
|
private Preferences userRoot; |
|
|
|
ExecutableInputMethodManager() { |
|
|
|
|
|
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() { |
|
|
|
while (!hasMultipleInputMethods()) { |
|
try { |
|
synchronized (this) { |
|
wait(); |
|
} |
|
} catch (InterruptedException e) { |
|
} |
|
} |
|
|
|
|
|
while (true) { |
|
waitForChangeRequest(); |
|
initializeInputMethodLocatorList(); |
|
try { |
|
if (requestComponent != null) { |
|
showInputMethodMenuOnRequesterEDT(requestComponent); |
|
} else { |
|
|
|
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 |
|
|
|
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 (requestComponent != null) |
|
return; |
|
|
|
requestComponent = comp; |
|
notify(); |
|
} |
|
|
|
public synchronized void notifyChangeRequestByHotKey(Component comp) { |
|
while (!(comp instanceof Frame || comp instanceof Dialog)) { |
|
if (comp == null) { |
|
|
|
return; |
|
} |
|
comp = comp.getParent(); |
|
} |
|
|
|
notifyChangeRequest(comp); |
|
} |
|
|
|
public String getTriggerMenuString() { |
|
return triggerMenuString; |
|
} |
|
|
|
|
|
|
|
*/ |
|
boolean hasMultipleInputMethods() { |
|
return ((hostAdapterLocator != null) && (javaInputMethodCount > 0) |
|
|| (javaInputMethodCount > 1)); |
|
} |
|
|
|
private synchronized void waitForChangeRequest() { |
|
try { |
|
while (requestComponent == null) { |
|
wait(); |
|
} |
|
} catch (InterruptedException e) { |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void initializeInputMethodLocatorList() { |
|
synchronized (javaInputMethodLocatorList) { |
|
javaInputMethodLocatorList.clear(); |
|
try { |
|
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { |
|
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()) { |
|
|
|
if (userRoot == null) { |
|
userRoot = getUserRoot(); |
|
} |
|
} else { |
|
|
|
triggerMenuString = null; |
|
} |
|
} |
|
|
|
private void showInputMethodMenu() { |
|
|
|
if (!hasMultipleInputMethods()) { |
|
requestComponent = null; |
|
return; |
|
} |
|
|
|
|
|
selectionMenu = InputMethodPopupMenu.getInstance(requestComponent, selectInputMethodMenuTitle); |
|
|
|
// we have to rebuild the menu each time because |
|
// some input methods (such as IIIMP) may change |
|
|
|
selectionMenu.removeAll(); |
|
|
|
// get information about the currently selected input method |
|
// ??? if there's no current input context, what's the point |
|
|
|
String currentSelection = getCurrentSelection(); |
|
|
|
|
|
if (hostAdapterLocator != null) { |
|
selectionMenu.addOneInputMethodToMenu(hostAdapterLocator, currentSelection); |
|
selectionMenu.addSeparator(); |
|
} |
|
|
|
|
|
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); |
|
requestComponent = null; |
|
} |
|
} |
|
|
|
private String getCurrentSelection() { |
|
InputContext inputContext = currentInputContext; |
|
if (inputContext != null) { |
|
InputMethodLocator locator = inputContext.getInputMethodLocator(); |
|
if (locator != null) { |
|
return locator.getActionCommandString(); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
synchronized void changeInputMethod(String choice) { |
|
InputMethodLocator locator = null; |
|
|
|
String inputMethodName = choice; |
|
String localeString = null; |
|
int index = choice.indexOf('\n'); |
|
if (index != -1) { |
|
localeString = choice.substring(index + 1); |
|
inputMethodName = choice.substring(0, index); |
|
} |
|
if (hostAdapterLocator.getActionCommandString().equals(inputMethodName)) { |
|
locator = hostAdapterLocator; |
|
} else { |
|
for (int i = 0; i < javaInputMethodLocatorList.size(); i++) { |
|
InputMethodLocator candidate = javaInputMethodLocatorList.get(i); |
|
String name = candidate.getActionCommandString(); |
|
if (name.equals(inputMethodName)) { |
|
locator = candidate; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (locator != null && localeString != null) { |
|
String language = "", country = "", variant = ""; |
|
int postIndex = localeString.indexOf('_'); |
|
if (postIndex == -1) { |
|
language = localeString; |
|
} else { |
|
language = localeString.substring(0, postIndex); |
|
int preIndex = postIndex + 1; |
|
postIndex = localeString.indexOf('_', preIndex); |
|
if (postIndex == -1) { |
|
country = localeString.substring(preIndex); |
|
} else { |
|
country = localeString.substring(preIndex, postIndex); |
|
variant = localeString.substring(postIndex + 1); |
|
} |
|
} |
|
Locale locale = new Locale(language, country, variant); |
|
locator = locator.deriveLocator(locale); |
|
} |
|
|
|
if (locator == null) |
|
return; |
|
|
|
|
|
if (requestInputContext != null) { |
|
requestInputContext.changeInputMethod(locator); |
|
requestInputContext = null; |
|
|
|
|
|
putPreferredInputMethod(locator); |
|
} |
|
} |
|
|
|
InputMethodLocator findInputMethod(Locale locale) { |
|
|
|
InputMethodLocator locator = getPreferredInputMethod(locale); |
|
if (locator != null) { |
|
return locator; |
|
} |
|
|
|
if (hostAdapterLocator != null && hostAdapterLocator.isLocaleAvailable(locale)) { |
|
return hostAdapterLocator.deriveLocator(locale); |
|
} |
|
|
|
|
|
initializeInputMethodLocatorList(); |
|
|
|
for (int i = 0; i < javaInputMethodLocatorList.size(); i++) { |
|
InputMethodLocator candidate = javaInputMethodLocatorList.get(i); |
|
if (candidate.isLocaleAvailable(locale)) { |
|
return candidate.deriveLocator(locale); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
Locale getDefaultKeyboardLocale() { |
|
Toolkit toolkit = Toolkit.getDefaultToolkit(); |
|
if (toolkit instanceof InputMethodSupport) { |
|
return ((InputMethodSupport)toolkit).getDefaultKeyboardLocale(); |
|
} else { |
|
return Locale.getDefault(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private synchronized InputMethodLocator getPreferredInputMethod(Locale locale) { |
|
InputMethodLocator preferredLocator = null; |
|
|
|
if (!hasMultipleInputMethods()) { |
|
|
|
return null; |
|
} |
|
|
|
|
|
preferredLocator = preferredLocatorCache.get(locale.toString().intern()); |
|
if (preferredLocator != null) { |
|
return preferredLocator; |
|
} |
|
|
|
|
|
String nodePath = findPreferredInputMethodNode(locale); |
|
String descriptorName = readPreferredInputMethod(nodePath); |
|
Locale advertised; |
|
|
|
|
|
if (descriptorName != null) { |
|
|
|
if (hostAdapterLocator != null && |
|
hostAdapterLocator.getDescriptor().getClass().getName().equals(descriptorName)) { |
|
advertised = getAdvertisedLocale(hostAdapterLocator, locale); |
|
if (advertised != null) { |
|
preferredLocator = hostAdapterLocator.deriveLocator(advertised); |
|
preferredLocatorCache.put(locale.toString().intern(), preferredLocator); |
|
} |
|
return preferredLocator; |
|
} |
|
|
|
for (int i = 0; i < javaInputMethodLocatorList.size(); i++) { |
|
InputMethodLocator locator = javaInputMethodLocatorList.get(i); |
|
InputMethodDescriptor descriptor = locator.getDescriptor(); |
|
if (descriptor.getClass().getName().equals(descriptorName)) { |
|
advertised = getAdvertisedLocale(locator, locale); |
|
if (advertised != null) { |
|
preferredLocator = locator.deriveLocator(advertised); |
|
preferredLocatorCache.put(locale.toString().intern(), preferredLocator); |
|
} |
|
return preferredLocator; |
|
} |
|
} |
|
|
|
|
|
writePreferredInputMethod(nodePath, null); |
|
} |
|
|
|
return null; |
|
} |
|
|
|
private String findPreferredInputMethodNode(Locale locale) { |
|
if (userRoot == null) { |
|
return null; |
|
} |
|
|
|
|
|
String nodePath = preferredIMNode + "/" + createLocalePath(locale); |
|
|
|
|
|
while (!nodePath.equals(preferredIMNode)) { |
|
try { |
|
if (userRoot.nodeExists(nodePath)) { |
|
if (readPreferredInputMethod(nodePath) != null) { |
|
return nodePath; |
|
} |
|
} |
|
} catch (BackingStoreException bse) { |
|
} |
|
|
|
|
|
nodePath = nodePath.substring(0, nodePath.lastIndexOf('/')); |
|
} |
|
|
|
return null; |
|
} |
|
|
|
private String readPreferredInputMethod(String nodePath) { |
|
if ((userRoot == null) || (nodePath == null)) { |
|
return null; |
|
} |
|
|
|
return userRoot.node(nodePath).get(descriptorKey, null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private synchronized void putPreferredInputMethod(InputMethodLocator locator) { |
|
InputMethodDescriptor descriptor = locator.getDescriptor(); |
|
Locale preferredLocale = locator.getLocale(); |
|
|
|
if (preferredLocale == null) { |
|
|
|
try { |
|
Locale[] availableLocales = descriptor.getAvailableLocales(); |
|
if (availableLocales.length == 1) { |
|
preferredLocale = availableLocales[0]; |
|
} else { |
|
|
|
return; |
|
} |
|
} catch (AWTException ae) { |
|
|
|
return; |
|
} |
|
} |
|
|
|
// for regions that have only one language, we need to regard |
|
|
|
if (preferredLocale.equals(Locale.JAPAN)) { |
|
preferredLocale = Locale.JAPANESE; |
|
} |
|
if (preferredLocale.equals(Locale.KOREA)) { |
|
preferredLocale = Locale.KOREAN; |
|
} |
|
if (preferredLocale.equals(new Locale("th", "TH"))) { |
|
preferredLocale = new Locale("th"); |
|
} |
|
|
|
|
|
String path = preferredIMNode + "/" + createLocalePath(preferredLocale); |
|
|
|
|
|
writePreferredInputMethod(path, descriptor.getClass().getName()); |
|
preferredLocatorCache.put(preferredLocale.toString().intern(), |
|
locator.deriveLocator(preferredLocale)); |
|
|
|
return; |
|
} |
|
|
|
private String createLocalePath(Locale locale) { |
|
String language = locale.getLanguage(); |
|
String country = locale.getCountry(); |
|
String variant = locale.getVariant(); |
|
String localePath = null; |
|
if (!variant.equals("")) { |
|
localePath = "_" + language + "/_" + country + "/_" + variant; |
|
} else if (!country.equals("")) { |
|
localePath = "_" + language + "/_" + country; |
|
} else { |
|
localePath = "_" + language; |
|
} |
|
|
|
return localePath; |
|
} |
|
|
|
private void writePreferredInputMethod(String path, String descriptorName) { |
|
if (userRoot != null) { |
|
Preferences node = userRoot.node(path); |
|
|
|
|
|
if (descriptorName != null) { |
|
node.put(descriptorKey, descriptorName); |
|
} else { |
|
node.remove(descriptorKey); |
|
} |
|
} |
|
} |
|
|
|
private Preferences getUserRoot() { |
|
return AccessController.doPrivileged(new PrivilegedAction<Preferences>() { |
|
public Preferences run() { |
|
return Preferences.userRoot(); |
|
} |
|
}); |
|
} |
|
|
|
private Locale getAdvertisedLocale(InputMethodLocator locator, Locale locale) { |
|
Locale advertised = null; |
|
|
|
if (locator.isLocaleAvailable(locale)) { |
|
advertised = locale; |
|
} else if (locale.getLanguage().equals("ja")) { |
|
// for Japanese, Korean, and Thai, check whether the input method supports |
|
|
|
if (locator.isLocaleAvailable(Locale.JAPAN)) { |
|
advertised = Locale.JAPAN; |
|
} else if (locator.isLocaleAvailable(Locale.JAPANESE)) { |
|
advertised = Locale.JAPANESE; |
|
} |
|
} else if (locale.getLanguage().equals("ko")) { |
|
if (locator.isLocaleAvailable(Locale.KOREA)) { |
|
advertised = Locale.KOREA; |
|
} else if (locator.isLocaleAvailable(Locale.KOREAN)) { |
|
advertised = Locale.KOREAN; |
|
} |
|
} else if (locale.getLanguage().equals("th")) { |
|
if (locator.isLocaleAvailable(new Locale("th", "TH"))) { |
|
advertised = new Locale("th", "TH"); |
|
} else if (locator.isLocaleAvailable(new Locale("th"))) { |
|
advertised = new Locale("th"); |
|
} |
|
} |
|
|
|
return advertised; |
|
} |
|
} |