GNU Classpath (0.18) | ||
Frames | No Frames |
1: /* InputContext.java -- provides the context for text input 2: Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.awt.im; 40: 41: import gnu.java.util.EmptyEnumeration; 42: 43: import java.awt.AWTEvent; 44: import java.awt.AWTException; 45: import java.awt.Component; 46: import java.awt.im.spi.InputMethod; 47: import java.awt.im.spi.InputMethodDescriptor; 48: import java.io.BufferedReader; 49: import java.io.IOException; 50: import java.io.InputStreamReader; 51: import java.net.URL; 52: import java.util.ArrayList; 53: import java.util.Enumeration; 54: import java.util.HashMap; 55: import java.util.Locale; 56: 57: /** 58: * Provides a context for controlling input methods and keyboard layouts. 59: * This class provides the communication layer between the client component, 60: * and the various locale-dependent text entry input methods that can be used 61: * for the client. By default, there is one instance per Window, shared among 62: * all components, but this limits text entry to one component at a time. 63: * Thus, text components can create their own instance to allow text entry 64: * in multiple components at a time. 65: * 66: * <p>By using the interfaces of {@link java.awt.im.spi}, you can install 67: * extensions which allow additional input methods. Some of these may use 68: * platform native input methods, or keyboard layouts provided by the platform. 69: * Input methods are unavailable if none have been installed and the platform 70: * has no underlying native input methods. Extensions are installed as jar 71: * files, usually accessed in the default extension location or specified by 72: * the -extdir VM flag. The jar must contain a file named 73: * "META_INF/services/java.awt.im.spi.InputMethodDescriptor" which lists, 74: * one entry per line in UTF-8 encoding, each class in the jar that implements 75: * java.awt.im.spi.InputMethodDescriptor. 76: * 77: * @author Eric Blake (ebb9@email.byu.edu) 78: * @see Component#getInputContext() 79: * @see Component#enableInputMethods(boolean) 80: * @since 1.2 81: * @status updated to 1.4, but unverified 82: */ 83: public class InputContext 84: { 85: /** 86: * The list of installed input method descriptors. 87: */ 88: private static final ArrayList descriptors = new ArrayList(); 89: static 90: { 91: Enumeration e; 92: try 93: { 94: e = ClassLoader.getSystemResources 95: ("META_INF/services/java.awt.im.spi.InputMethodDescriptor"); 96: } 97: catch (IOException ex) 98: { 99: // XXX Should we do something else? 100: e = EmptyEnumeration.getInstance(); 101: } 102: while (e.hasMoreElements()) 103: { 104: URL url = (URL) e.nextElement(); 105: BufferedReader in; 106: String line; 107: try 108: { 109: in = new BufferedReader 110: (new InputStreamReader(url.openConnection().getInputStream(), 111: "UTF-8")); 112: line = in.readLine().trim(); 113: } 114: catch (IOException ignored) 115: { 116: continue; 117: } 118: outer: 119: while (line != null) 120: { 121: try 122: { 123: if (line.charAt(0) != '#') 124: { 125: Class c = Class.forName(line); 126: descriptors.add((InputMethodDescriptor) c.newInstance()); 127: } 128: line = in.readLine().trim(); 129: } 130: catch (IOException ex) 131: { 132: continue outer; 133: } 134: catch (Exception ignored) 135: { 136: } 137: } 138: } 139: } 140: 141: /** The current input method; null if no input methods are installed. */ 142: private InputMethod im; 143: 144: /** Map of locales to the most recently selected input method. */ 145: private final HashMap recent = new HashMap(); 146: 147: /** The list of acceptable character subsets. */ 148: private Character.Subset[] subsets; 149: 150: /** 151: * Construct an InputContext. This is protected, so clients must use 152: * {@link #getInstance()} instead. 153: */ 154: protected InputContext() 155: { 156: } 157: 158: /** 159: * Returns a new InputContext. 160: * 161: * @return a new instance, initialized to the default locale if available 162: */ 163: public static InputContext getInstance() 164: { 165: InputContext ic = new InputContext(); 166: ic.selectInputMethod(Locale.getDefault()); 167: return ic; 168: } 169: 170: /** 171: * Attempts to select an input method or keyboard layout which supports the 172: * given locale. This returns true if a locale is available and was selected. 173: * The following steps are taken in choosing an input method:<ul> 174: * <li>If the currently selected input method or keyboard layout supports 175: * the requested locale, it remains selected.</li> 176: * <li>If there is no input method or keyboard layout available that 177: * supports the requested locale, the current input method or keyboard 178: * layout remains selected.</li> 179: * <li>If the user has previously selected an input method or keyboard 180: * layout for the requested locale from the user interface, then the most 181: * recently selected such input method or keyboard layout is reselected.</li> 182: * <li>Otherwise, an input method or keyboard layout that supports the 183: * requested locale is selected in an implementation dependent way. This 184: * implementation chooses the first input method which supports the requested 185: * locale based on the InputMethodDescriptors loaded from the extensions 186: * installed on the CLASSPATH.</li> 187: * </ul> 188: * 189: * <p>Before switching away from an input method, any currently uncommitted 190: * text is committed. Not all host operating systems provide API to 191: * determine the locale of the currently selected native input method or 192: * keyboard layout, and to select a native input method or keyboard layout 193: * by locale. For host operating systems that don't provide such API, 194: * selectInputMethod assumes that native input methods or keyboard layouts 195: * provided by the host operating system support only the system's default 196: * locale. 197: * 198: * <p>An example of where this may be called is in a multi-language document, 199: * when moving the insertion point between sections of different locale, so 200: * that the user may use the input method appropriate to that section of the 201: * document. 202: * 203: * @param locale the desired new locale 204: * @return true if the new locale is active 205: * @throws NullPointerException if locale is null 206: */ 207: public boolean selectInputMethod(Locale locale) 208: { 209: if (im != null && im.setLocale(locale)) 210: { 211: recent.put(locale, im); 212: return true; 213: } 214: InputMethod next = (InputMethod) recent.get(locale); 215: outer: 216: if (next != null) 217: for (int i = 0, limit = descriptors.size(); i < limit; i++) 218: { 219: InputMethodDescriptor d = (InputMethodDescriptor) descriptors.get(i); 220: Locale[] list; 221: try 222: { 223: list = d.getAvailableLocales(); 224: } 225: catch (AWTException ignored) 226: { 227: continue; 228: } 229: for (int j = list.length; --j >= 0; ) 230: if (locale.equals(list[j])) 231: { 232: try 233: { 234: next = d.createInputMethod(); 235: recent.put(locale, next); 236: } 237: catch (Exception ignored) 238: { 239: continue; 240: } 241: } 242: } 243: if (next == null) 244: return false; 245: // XXX I'm not sure if this does all the necessary steps in the switch. 246: if (im != null) 247: { 248: try 249: { 250: next.setCompositionEnabled(im.isCompositionEnabled()); 251: } 252: catch (UnsupportedOperationException ignored) 253: { 254: } 255: im.endComposition(); 256: im.deactivate(false); 257: im.hideWindows(); 258: } 259: im = next; 260: im.setLocale(locale); 261: im.setCharacterSubsets(subsets); 262: return true; 263: } 264: 265: /** 266: * Returns the current locale of the current input method or keyboard 267: * layout. Returns null if the input context does not have a current input 268: * method or keyboard layout or if the current input method's 269: * {@link InputMethod#getLocale()} method returns null. Not all host 270: * operating systems provide API to determine the locale of the currently 271: * selected native input method or keyboard layout. For host operating 272: * systems that don't provide such API, getLocale assumes that the current 273: * locale of all native input methods or keyboard layouts provided by the 274: * host operating system is the system's default locale. 275: * 276: * @return the locale of the current input method, or null 277: * @since 1.3 278: */ 279: public Locale getLocale() 280: { 281: return im == null ? null : im.getLocale(); 282: } 283: 284: /** 285: * Sets the subsets of Unicode characters allowed to be input by the current 286: * input method, as well as subsequent input methods. The value of null 287: * implies all characters are legal. Applications should not rely on this 288: * behavior, since native host input methods may not allow restrictions. 289: * If no current input method is available, this has no immediate effect. 290: * 291: * @param subsets the set of Unicode subsets to accept, or null 292: */ 293: public void setCharacterSubsets(Character.Subset[] subsets) 294: { 295: this.subsets = subsets; 296: if (im != null) 297: im.setCharacterSubsets(subsets); 298: } 299: 300: /** 301: * Changes the enabled status of the current input method. An input method 302: * that is enabled for composition interprets incoming events for both 303: * composition and control purposes, while a disabled input method only 304: * interprets control commands (including commands to enable itself). 305: * 306: * @param enable whether to enable the input method 307: * @throws UnsupportedOperationException if there is no current input method, 308: * or the input method does not support enabling 309: * @see #isCompositionEnabled() 310: * @since 1.3 311: */ 312: public void setCompositionEnabled(boolean enable) 313: { 314: if (im == null) 315: throw new UnsupportedOperationException(); 316: im.setCompositionEnabled(enable); 317: } 318: 319: /** 320: * Find out if the current input method is enabled. 321: * 322: * @return true if the current input method is enabled 323: * @throws UnsupportedOperationException if there is no current input method, 324: * or the input method does not support enabling 325: * @see #setCompositionEnabled(boolean) 326: * @since 1.3 327: */ 328: public boolean isCompositionEnabled() 329: { 330: if (im == null) 331: throw new UnsupportedOperationException(); 332: return im.isCompositionEnabled(); 333: } 334: 335: /** 336: * Starts a reconversion operation in the current input method. The input 337: * method gets theh text to reconvert from the client component, using 338: * {@link InputMethodRequests#getSelectedText(Attribute[])}. Then the 339: * composed and committed text produced by the operation is sent back to 340: * the client using a sequence of InputMethodRequests. 341: * 342: * @throws UnsupportedOperationException if there is no current input method, 343: * or the input method does not support reconversion 344: * @since 1.3 345: */ 346: public void reconvert() 347: { 348: if (im == null) 349: throw new UnsupportedOperationException(); 350: im.reconvert(); 351: } 352: 353: /** 354: * Dispatches an event to the current input method. This is called 355: * automatically by AWT. If no input method is available, then the event 356: * will never be consumed. 357: * 358: * @param event the event to dispatch 359: * @throws NullPointerException if event is null 360: */ 361: public void dispatchEvent(AWTEvent event) 362: { 363: if (im != null) 364: im.dispatchEvent(event); 365: } 366: 367: /** 368: * Notifies the input context that a client component has been removed from 369: * its containment hierarchy, or that input method support has been disabled 370: * for the component. This method is usually called from the client 371: * component's {@link Component#removeNotify()} method. Potentially pending 372: * input from input methods for this component is discarded. If no input 373: * methods are available, then this method has no effect. 374: * 375: * @param client the client component 376: * @throws NullPointerException if client is null 377: */ 378: public void removeNotify(Component client) 379: { 380: // XXX What to do with client information? 381: if (im != null) 382: { 383: im.deactivate(false); 384: im.removeNotify(); 385: } 386: } 387: 388: /** 389: * Ends any input composition that may currently be going on in this 390: * context. Depending on the platform and possibly user preferences, this 391: * may commit or delete uncommitted text. Any changes to the text are 392: * communicated to the active component using an input method event. If no 393: * input methods are available, then this method has no effect. This may 394: * be called for a variety of reasons, such as when the user moves the 395: * insertion point in the client text outside the range of the composed text, 396: * or when text is saved to file. 397: */ 398: public void endComposition() 399: { 400: if (im != null) 401: im.endComposition(); 402: } 403: 404: /** 405: * Disposes of the input context and release the resources used by it. 406: * Called automatically by AWT for the default input context of each 407: * Window. If no input methods are available, then this method has no 408: * effect. 409: */ 410: public void dispose() 411: { 412: if (im != null) 413: { 414: im.deactivate(false); 415: im.dispose(); 416: } 417: } 418: 419: /** 420: * Returns a control object from the current input method, or null. A 421: * control object provides implementation-dependent methods that control 422: * the behavior of the input method or obtain information from the input 423: * method. Clients have to compare the result against known input method 424: * control object types. If no input methods are available or the current 425: * input method does not provide an input method control object, then null 426: * is returned. 427: * 428: * @return the control object, or null 429: */ 430: public Object getInputMethodControlObject() 431: { 432: return im == null ? null : im.getControlObject(); 433: } 434: } // class InputContext
GNU Classpath (0.18) |