0001: /*
0002: * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.awt.im;
0027:
0028: import java.awt.AWTEvent;
0029: import java.awt.AWTKeyStroke;
0030: import java.awt.Component;
0031: import java.awt.EventQueue;
0032: import java.awt.Frame;
0033: import java.awt.Rectangle;
0034: import java.awt.Toolkit;
0035: import java.awt.Window;
0036: import java.awt.event.ComponentEvent;
0037: import java.awt.event.ComponentListener;
0038: import java.awt.event.FocusEvent;
0039: import java.awt.event.InputEvent;
0040: import java.awt.event.InputMethodEvent;
0041: import java.awt.event.KeyEvent;
0042: import java.awt.event.WindowEvent;
0043: import java.awt.event.WindowListener;
0044: import java.awt.im.InputMethodRequests;
0045: import java.awt.im.spi.InputMethod;
0046: import java.lang.Character.Subset;
0047: import java.security.AccessController;
0048: import java.security.PrivilegedAction;
0049: import java.text.MessageFormat;
0050: import java.util.HashMap;
0051: import java.util.Iterator;
0052: import java.util.Locale;
0053: import java.util.logging.*;
0054: import java.util.prefs.BackingStoreException;
0055: import java.util.prefs.Preferences;
0056: import sun.awt.SunToolkit;
0057:
0058: /**
0059: * This InputContext class contains parts of the implementation of
0060: * java.text.im.InputContext. These parts have been moved
0061: * here to avoid exposing protected members that are needed by the
0062: * subclass InputMethodContext.
0063: *
0064: * @see java.awt.im.InputContext
0065: * @version 1.63 05/05/07
0066: * @author JavaSoft Asia/Pacific
0067: */
0068:
0069: public class InputContext extends java.awt.im.InputContext implements
0070: ComponentListener, WindowListener {
0071: private static final Logger log = Logger
0072: .getLogger("sun.awt.im.InputContext");
0073: // The current input method is represented by two objects:
0074: // a locator is used to keep information about the selected
0075: // input method and locale until we actually need a real input
0076: // method; only then the input method itself is created.
0077: // Once there is an input method, the input method's locale
0078: // takes precedence over locale information in the locator.
0079: private InputMethodLocator inputMethodLocator;
0080: private InputMethod inputMethod;
0081: private boolean inputMethodCreationFailed;
0082:
0083: // holding bin for previously used input method instances, but not the current one
0084: private HashMap usedInputMethods;
0085:
0086: // the current client component is kept until the user focusses on a different
0087: // client component served by the same input context. When that happens, we call
0088: // endComposition so that text doesn't jump from one component to another.
0089: private Component currentClientComponent;
0090: private Component awtFocussedComponent;
0091: private boolean isInputMethodActive;
0092: private Subset[] characterSubsets = null;
0093:
0094: // true if composition area has been set to invisible when focus was lost
0095: private boolean compositionAreaHidden = false;
0096:
0097: // The input context for whose input method we may have to call hideWindows
0098: private static InputContext inputMethodWindowContext;
0099:
0100: // Previously active input method to decide whether we need to call
0101: // InputMethodAdapter.stopListening() on activateInputMethod()
0102: private static InputMethod previousInputMethod = null;
0103:
0104: // true if the current input method requires client window change notification
0105: private boolean clientWindowNotificationEnabled = false;
0106: // client window to which this input context is listening
0107: private Window clientWindowListened;
0108: // cache location notification
0109: private Rectangle clientWindowLocation = null;
0110: // holding the state of clientWindowNotificationEnabled of only non-current input methods
0111: private HashMap perInputMethodState;
0112:
0113: // Input Method selection hot key stuff
0114: private static AWTKeyStroke inputMethodSelectionKey;
0115: private static boolean inputMethodSelectionKeyInitialized = false;
0116: private static final String inputMethodSelectionKeyPath = "/java/awt/im/selectionKey";
0117: private static final String inputMethodSelectionKeyCodeName = "keyCode";
0118: private static final String inputMethodSelectionKeyModifiersName = "modifiers";
0119:
0120: /**
0121: * Constructs an InputContext.
0122: */
0123: protected InputContext() {
0124: InputMethodManager imm = InputMethodManager.getInstance();
0125: synchronized (InputContext.class) {
0126: if (!inputMethodSelectionKeyInitialized) {
0127: inputMethodSelectionKeyInitialized = true;
0128: if (imm.hasMultipleInputMethods()) {
0129: initializeInputMethodSelectionKey();
0130: }
0131: }
0132: }
0133: selectInputMethod(imm.getDefaultKeyboardLocale());
0134: }
0135:
0136: /**
0137: * @see java.awt.im.InputContext#selectInputMethod
0138: * @exception NullPointerException when the locale is null.
0139: */
0140: public synchronized boolean selectInputMethod(Locale locale) {
0141: if (locale == null) {
0142: throw new NullPointerException();
0143: }
0144:
0145: // see whether the current input method supports the locale
0146: if (inputMethod != null) {
0147: if (inputMethod.setLocale(locale)) {
0148: return true;
0149: }
0150: } else if (inputMethodLocator != null) {
0151: // This is not 100% correct, since the input method
0152: // may support the locale without advertising it.
0153: // But before we try instantiations and setLocale,
0154: // we look for an input method that's more confident.
0155: if (inputMethodLocator.isLocaleAvailable(locale)) {
0156: inputMethodLocator = inputMethodLocator
0157: .deriveLocator(locale);
0158: return true;
0159: }
0160: }
0161:
0162: // see whether there's some other input method that supports the locale
0163: InputMethodLocator newLocator = InputMethodManager
0164: .getInstance().findInputMethod(locale);
0165: if (newLocator != null) {
0166: changeInputMethod(newLocator);
0167: return true;
0168: }
0169:
0170: // make one last desperate effort with the current input method
0171: // ??? is this good? This is pretty high cost for something that's likely to fail.
0172: if (inputMethod == null && inputMethodLocator != null) {
0173: inputMethod = getInputMethod();
0174: if (inputMethod != null) {
0175: return inputMethod.setLocale(locale);
0176: }
0177: }
0178: return false;
0179: }
0180:
0181: /**
0182: * @see java.awt.im.InputContext#getLocale
0183: */
0184: public Locale getLocale() {
0185: if (inputMethod != null) {
0186: return inputMethod.getLocale();
0187: } else if (inputMethodLocator != null) {
0188: return inputMethodLocator.getLocale();
0189: } else {
0190: return null;
0191: }
0192: }
0193:
0194: /**
0195: * @see java.awt.im.InputContext#setCharacterSubsets
0196: */
0197: public void setCharacterSubsets(Subset[] subsets) {
0198: if (subsets == null) {
0199: characterSubsets = null;
0200: } else {
0201: characterSubsets = new Subset[subsets.length];
0202: System.arraycopy(subsets, 0, characterSubsets, 0,
0203: characterSubsets.length);
0204: }
0205: if (inputMethod != null) {
0206: inputMethod.setCharacterSubsets(subsets);
0207: }
0208: }
0209:
0210: /**
0211: * @see java.awt.im.InputContext#reconvert
0212: * @since 1.3
0213: * @exception UnsupportedOperationException when input method is null
0214: */
0215: public synchronized void reconvert() {
0216: InputMethod inputMethod = getInputMethod();
0217: if (inputMethod == null) {
0218: throw new UnsupportedOperationException();
0219: }
0220: inputMethod.reconvert();
0221: }
0222:
0223: /**
0224: * @see java.awt.im.InputContext#dispatchEvent
0225: */
0226: public void dispatchEvent(AWTEvent event) {
0227:
0228: if (event instanceof InputMethodEvent) {
0229: return;
0230: }
0231:
0232: // Ignore focus events that relate to the InputMethodWindow of this context.
0233: // This is a workaround. Should be removed after 4452384 is fixed.
0234: if (event instanceof FocusEvent) {
0235: Component opposite = ((FocusEvent) event)
0236: .getOppositeComponent();
0237: if ((opposite != null)
0238: && (getComponentWindow(opposite) instanceof InputMethodWindow)
0239: && (opposite.getInputContext() == this )) {
0240: return;
0241: }
0242: }
0243:
0244: InputMethod inputMethod = getInputMethod();
0245: int id = event.getID();
0246:
0247: switch (id) {
0248: case FocusEvent.FOCUS_GAINED:
0249: focusGained((Component) event.getSource());
0250: break;
0251:
0252: case FocusEvent.FOCUS_LOST:
0253: focusLost((Component) event.getSource(),
0254: ((FocusEvent) event).isTemporary());
0255: break;
0256:
0257: case KeyEvent.KEY_PRESSED:
0258: if (checkInputMethodSelectionKey((KeyEvent) event)) {
0259: // pop up the input method selection menu
0260: InputMethodManager.getInstance()
0261: .notifyChangeRequestByHotKey(
0262: (Component) event.getSource());
0263: break;
0264: }
0265:
0266: // fall through
0267:
0268: default:
0269: if ((inputMethod != null) && (event instanceof InputEvent)) {
0270: inputMethod.dispatchEvent(event);
0271: }
0272: }
0273: }
0274:
0275: /**
0276: * Handles focus gained events for any component that's using
0277: * this input context.
0278: * These events are generated by AWT when the keyboard focus
0279: * moves to a component.
0280: * Besides actual client components, the source components
0281: * may also be the composition area or any component in an
0282: * input method window.
0283: * <p>
0284: * When handling the focus event for a client component, this
0285: * method checks whether the input context was previously
0286: * active for a different client component, and if so, calls
0287: * endComposition for the previous client component.
0288: *
0289: * @param source the component gaining the focus
0290: */
0291: private void focusGained(Component source) {
0292:
0293: /*
0294: * NOTE: When a Container is removing its Component which
0295: * invokes this.removeNotify(), the Container has the global
0296: * Component lock. It is possible to happen that an
0297: * application thread is calling this.removeNotify() while an
0298: * AWT event queue thread is dispatching a focus event via
0299: * this.dispatchEvent(). If an input method uses AWT
0300: * components (e.g., IIIMP status window), it causes deadlock,
0301: * for example, Component.show()/hide() in this situation
0302: * because hide/show tried to obtain the lock. Therefore,
0303: * it's necessary to obtain the global Component lock before
0304: * activating or deactivating an input method.
0305: */
0306: synchronized (source.getTreeLock()) {
0307: synchronized (this ) {
0308: if (source instanceof CompositionArea) {
0309: // no special handling for this one
0310: } else if (getComponentWindow(source) instanceof InputMethodWindow) {
0311: // no special handling for this one either
0312: } else {
0313: if (!source.isDisplayable()) {
0314: // Component is being disposed
0315: return;
0316: }
0317:
0318: // Focus went to a real client component.
0319: // Check whether we're switching between client components
0320: // that share an input context. We can't do that earlier
0321: // than here because we don't want to end composition
0322: // until we really know we're switching to a different component
0323: if (inputMethod != null) {
0324: if (currentClientComponent != null
0325: && currentClientComponent != source) {
0326: if (!isInputMethodActive) {
0327: activateInputMethod(false);
0328: }
0329: endComposition();
0330: deactivateInputMethod(false);
0331: }
0332: }
0333:
0334: currentClientComponent = source;
0335: }
0336:
0337: awtFocussedComponent = source;
0338: if (inputMethod instanceof InputMethodAdapter) {
0339: ((InputMethodAdapter) inputMethod)
0340: .setAWTFocussedComponent(source);
0341: }
0342:
0343: // it's possible that the input method is still active because
0344: // we suppressed a deactivate cause by an input method window
0345: // coming up
0346: if (!isInputMethodActive) {
0347: activateInputMethod(true);
0348: }
0349:
0350: // If the client component is an active client with the below-the-spot
0351: // input style, then make the composition window undecorated without a title bar.
0352: InputMethodContext inputContext = ((InputMethodContext) this );
0353: if (!inputContext.isCompositionAreaVisible()) {
0354: InputMethodRequests req = source
0355: .getInputMethodRequests();
0356: if (req != null
0357: && inputContext.useBelowTheSpotInput()) {
0358: inputContext
0359: .setCompositionAreaUndecorated(true);
0360: } else {
0361: inputContext
0362: .setCompositionAreaUndecorated(false);
0363: }
0364: }
0365: // restores the composition area if it was set to invisible
0366: // when focus got lost
0367: if (compositionAreaHidden == true) {
0368: ((InputMethodContext) this )
0369: .setCompositionAreaVisible(true);
0370: compositionAreaHidden = false;
0371: }
0372: }
0373: }
0374: }
0375:
0376: /**
0377: * Activates the current input method of this input context, and grabs
0378: * the composition area for use by this input context.
0379: * If updateCompositionArea is true, the text in the composition area
0380: * is updated (set to false if the text is going to change immediately
0381: * to avoid screen flicker).
0382: */
0383: private void activateInputMethod(boolean updateCompositionArea) {
0384: // call hideWindows() if this input context uses a different
0385: // input method than the previously activated one
0386: if (inputMethodWindowContext != null
0387: && inputMethodWindowContext != this
0388: && inputMethodWindowContext.inputMethodLocator != null
0389: && !inputMethodWindowContext.inputMethodLocator
0390: .sameInputMethod(inputMethodLocator)
0391: && inputMethodWindowContext.inputMethod != null) {
0392: inputMethodWindowContext.inputMethod.hideWindows();
0393: }
0394: inputMethodWindowContext = this ;
0395:
0396: if (inputMethod != null) {
0397: if (previousInputMethod != inputMethod
0398: && previousInputMethod instanceof InputMethodAdapter) {
0399: // let the host adapter pass through the input events for the
0400: // new input method
0401: ((InputMethodAdapter) previousInputMethod)
0402: .stopListening();
0403: }
0404: previousInputMethod = null;
0405:
0406: if (log.isLoggable(Level.FINE))
0407: log.fine("Current client component "
0408: + currentClientComponent);
0409: if (inputMethod instanceof InputMethodAdapter) {
0410: ((InputMethodAdapter) inputMethod)
0411: .setClientComponent(currentClientComponent);
0412: }
0413: inputMethod.activate();
0414: isInputMethodActive = true;
0415:
0416: if (perInputMethodState != null) {
0417: Boolean state = (Boolean) perInputMethodState
0418: .remove(inputMethod);
0419: if (state != null) {
0420: clientWindowNotificationEnabled = state
0421: .booleanValue();
0422: }
0423: }
0424: if (clientWindowNotificationEnabled) {
0425: if (!addedClientWindowListeners()) {
0426: addClientWindowListeners();
0427: }
0428: synchronized (this ) {
0429: if (clientWindowListened != null) {
0430: notifyClientWindowChange(clientWindowListened);
0431: }
0432: }
0433: } else {
0434: if (addedClientWindowListeners()) {
0435: removeClientWindowListeners();
0436: }
0437: }
0438: }
0439: InputMethodManager.getInstance().setInputContext(this );
0440:
0441: ((InputMethodContext) this )
0442: .grabCompositionArea(updateCompositionArea);
0443: }
0444:
0445: static Window getComponentWindow(Component component) {
0446: while (true) {
0447: if (component == null) {
0448: return null;
0449: } else if (component instanceof Window) {
0450: return (Window) component;
0451: } else {
0452: component = component.getParent();
0453: }
0454: }
0455: }
0456:
0457: /**
0458: * Handles focus lost events for any component that's using
0459: * this input context.
0460: * These events are generated by AWT when the keyboard focus
0461: * moves away from a component.
0462: * Besides actual client components, the source components
0463: * may also be the composition area or any component in an
0464: * input method window.
0465: *
0466: * @param source the component losing the focus
0467: * @isTemporary whether the focus change is temporary
0468: */
0469: private void focusLost(Component source, boolean isTemporary) {
0470:
0471: // see the note on synchronization in focusGained
0472: synchronized (source.getTreeLock()) {
0473: synchronized (this ) {
0474:
0475: // We need to suppress deactivation if removeNotify has been called earlier.
0476: // This is indicated by isInputMethodActive == false.
0477: if (isInputMethodActive) {
0478: deactivateInputMethod(isTemporary);
0479: }
0480:
0481: awtFocussedComponent = null;
0482: if (inputMethod instanceof InputMethodAdapter) {
0483: ((InputMethodAdapter) inputMethod)
0484: .setAWTFocussedComponent(null);
0485: }
0486:
0487: // hides the composition area if currently it is visible
0488: InputMethodContext inputContext = ((InputMethodContext) this );
0489: if (inputContext.isCompositionAreaVisible()) {
0490: inputContext.setCompositionAreaVisible(false);
0491: compositionAreaHidden = true;
0492: }
0493: }
0494: }
0495: }
0496:
0497: /**
0498: * Checks the key event is the input method selection key or not.
0499: */
0500: private boolean checkInputMethodSelectionKey(KeyEvent event) {
0501: if (inputMethodSelectionKey != null) {
0502: AWTKeyStroke aKeyStroke = AWTKeyStroke
0503: .getAWTKeyStrokeForEvent(event);
0504: return inputMethodSelectionKey.equals(aKeyStroke);
0505: } else {
0506: return false;
0507: }
0508: }
0509:
0510: private void deactivateInputMethod(boolean isTemporary) {
0511: InputMethodManager.getInstance().setInputContext(null);
0512: if (inputMethod != null) {
0513: isInputMethodActive = false;
0514: inputMethod.deactivate(isTemporary);
0515: previousInputMethod = inputMethod;
0516: }
0517: }
0518:
0519: /**
0520: * Switches from the current input method to the one described by newLocator.
0521: * The current input method, if any, is asked to end composition, deactivated,
0522: * and saved for future use. The newLocator is made the current locator. If
0523: * the input context is active, an input method instance for the new locator
0524: * is obtained; otherwise this is deferred until required.
0525: */
0526: synchronized void changeInputMethod(InputMethodLocator newLocator) {
0527: // If we don't have a locator yet, this must be a new input context.
0528: // If we created a new input method here, we might get into an
0529: // infinite loop: create input method -> create some input method window ->
0530: // create new input context -> add input context to input method manager's context list ->
0531: // call changeInputMethod on it.
0532: // So, just record the locator. dispatchEvent will create the input method when needed.
0533: if (inputMethodLocator == null) {
0534: inputMethodLocator = newLocator;
0535: inputMethodCreationFailed = false;
0536: return;
0537: }
0538:
0539: // If the same input method is specified, just keep it.
0540: // Adjust the locale if necessary.
0541: if (inputMethodLocator.sameInputMethod(newLocator)) {
0542: Locale newLocale = newLocator.getLocale();
0543: if (newLocale != null
0544: && inputMethodLocator.getLocale() != newLocale) {
0545: if (inputMethod != null) {
0546: inputMethod.setLocale(newLocale);
0547: }
0548: inputMethodLocator = newLocator;
0549: }
0550: return;
0551: }
0552:
0553: // Switch out the old input method
0554: Locale savedLocale = inputMethodLocator.getLocale();
0555: boolean wasInputMethodActive = isInputMethodActive;
0556: boolean wasCompositionEnabledSupported = false;
0557: boolean wasCompositionEnabled = false;
0558: if (inputMethod != null) {
0559: try {
0560: wasCompositionEnabled = inputMethod
0561: .isCompositionEnabled();
0562: wasCompositionEnabledSupported = true;
0563: } catch (UnsupportedOperationException e) {
0564: }
0565:
0566: if (currentClientComponent != null) {
0567: if (!isInputMethodActive) {
0568: activateInputMethod(false);
0569: }
0570: endComposition();
0571: deactivateInputMethod(false);
0572: if (inputMethod instanceof InputMethodAdapter) {
0573: ((InputMethodAdapter) inputMethod)
0574: .setClientComponent(null);
0575: }
0576: }
0577: savedLocale = inputMethod.getLocale();
0578:
0579: // keep the input method instance around for future use
0580: if (usedInputMethods == null) {
0581: usedInputMethods = new HashMap(5);
0582: }
0583: if (perInputMethodState == null) {
0584: perInputMethodState = new HashMap(5);
0585: }
0586: usedInputMethods
0587: .put(inputMethodLocator.deriveLocator(null),
0588: inputMethod);
0589: perInputMethodState.put(inputMethod, new Boolean(
0590: clientWindowNotificationEnabled));
0591: enableClientWindowNotification(inputMethod, false);
0592: if (this == inputMethodWindowContext) {
0593: inputMethod.hideWindows();
0594: inputMethodWindowContext = null;
0595: }
0596: inputMethodLocator = null;
0597: inputMethod = null;
0598: inputMethodCreationFailed = false;
0599: }
0600:
0601: // Switch in the new input method
0602: if (newLocator.getLocale() == null && savedLocale != null
0603: && newLocator.isLocaleAvailable(savedLocale)) {
0604: newLocator = newLocator.deriveLocator(savedLocale);
0605: }
0606: inputMethodLocator = newLocator;
0607: inputMethodCreationFailed = false;
0608:
0609: // activate the new input method if the old one was active
0610: if (wasInputMethodActive) {
0611: inputMethod = getInputMethodInstance();
0612: if (inputMethod instanceof InputMethodAdapter) {
0613: ((InputMethodAdapter) inputMethod)
0614: .setAWTFocussedComponent(awtFocussedComponent);
0615: }
0616: activateInputMethod(true);
0617: }
0618:
0619: // enable/disable composition if the old one supports querying enable/disable
0620: if (wasCompositionEnabledSupported) {
0621: inputMethod = getInputMethod();
0622: if (inputMethod != null) {
0623: try {
0624: inputMethod
0625: .setCompositionEnabled(wasCompositionEnabled);
0626: } catch (UnsupportedOperationException e) {
0627: }
0628: }
0629: }
0630: }
0631:
0632: /**
0633: * Returns the client component.
0634: */
0635: Component getClientComponent() {
0636: return currentClientComponent;
0637: }
0638:
0639: /**
0640: * @see java.awt.im.InputContext#removeNotify
0641: * @exception NullPointerException when the component is null.
0642: */
0643: public synchronized void removeNotify(Component component) {
0644: if (component == null) {
0645: throw new NullPointerException();
0646: }
0647:
0648: if (inputMethod == null) {
0649: if (component == currentClientComponent) {
0650: currentClientComponent = null;
0651: }
0652: return;
0653: }
0654:
0655: // We may or may not get a FOCUS_LOST event for this component,
0656: // so do the deactivation stuff here too.
0657: if (component == awtFocussedComponent) {
0658: focusLost(component, false);
0659: }
0660:
0661: if (component == currentClientComponent) {
0662: if (isInputMethodActive) {
0663: // component wasn't the one that had the focus
0664: deactivateInputMethod(false);
0665: }
0666: inputMethod.removeNotify();
0667: if (clientWindowNotificationEnabled
0668: && addedClientWindowListeners()) {
0669: removeClientWindowListeners();
0670: }
0671: currentClientComponent = null;
0672: if (inputMethod instanceof InputMethodAdapter) {
0673: ((InputMethodAdapter) inputMethod)
0674: .setClientComponent(null);
0675: }
0676:
0677: // removeNotify() can be issued from a thread other than the event dispatch
0678: // thread. In that case, avoid possible deadlock between Component.AWTTreeLock
0679: // and InputMethodContext.compositionAreaHandlerLock by releasing the composition
0680: // area on the event dispatch thread.
0681: if (EventQueue.isDispatchThread()) {
0682: ((InputMethodContext) this ).releaseCompositionArea();
0683: } else {
0684: EventQueue.invokeLater(new Runnable() {
0685: public void run() {
0686: ((InputMethodContext) InputContext.this )
0687: .releaseCompositionArea();
0688: }
0689: });
0690: }
0691: }
0692: }
0693:
0694: /**
0695: * @see java.awt.im.InputContext#dispose
0696: * @exception IllegalStateException when the currentClientComponent is not null
0697: */
0698: public synchronized void dispose() {
0699: if (currentClientComponent != null) {
0700: throw new IllegalStateException(
0701: "Can't dispose InputContext while it's active");
0702: }
0703: if (inputMethod != null) {
0704: if (this == inputMethodWindowContext) {
0705: inputMethod.hideWindows();
0706: inputMethodWindowContext = null;
0707: }
0708: if (inputMethod == previousInputMethod) {
0709: previousInputMethod = null;
0710: }
0711: if (clientWindowNotificationEnabled) {
0712: if (addedClientWindowListeners()) {
0713: removeClientWindowListeners();
0714: }
0715: clientWindowNotificationEnabled = false;
0716: }
0717: inputMethod.dispose();
0718:
0719: // in case the input method enabled the client window
0720: // notification in dispose(), which shouldn't happen, it
0721: // needs to be cleaned up again.
0722: if (clientWindowNotificationEnabled) {
0723: enableClientWindowNotification(inputMethod, false);
0724: }
0725:
0726: inputMethod = null;
0727: }
0728: inputMethodLocator = null;
0729: if (usedInputMethods != null && !usedInputMethods.isEmpty()) {
0730: Iterator iterator = usedInputMethods.values().iterator();
0731: usedInputMethods = null;
0732: while (iterator.hasNext()) {
0733: ((InputMethod) iterator.next()).dispose();
0734: }
0735: }
0736:
0737: // cleanup client window notification variables
0738: clientWindowNotificationEnabled = false;
0739: clientWindowListened = null;
0740: perInputMethodState = null;
0741: }
0742:
0743: /**
0744: * @see java.awt.im.InputContext#getInputMethodControlObject
0745: */
0746: public synchronized Object getInputMethodControlObject() {
0747: InputMethod inputMethod = getInputMethod();
0748:
0749: if (inputMethod != null) {
0750: return inputMethod.getControlObject();
0751: } else {
0752: return null;
0753: }
0754: }
0755:
0756: /**
0757: * @see java.awt.im.InputContext#setCompositionEnabled(boolean)
0758: * @exception UnsupportedOperationException when input method is null
0759: */
0760: public void setCompositionEnabled(boolean enable) {
0761: InputMethod inputMethod = getInputMethod();
0762:
0763: if (inputMethod == null) {
0764: throw new UnsupportedOperationException();
0765: }
0766: inputMethod.setCompositionEnabled(enable);
0767: }
0768:
0769: /**
0770: * @see java.awt.im.InputContext#isCompositionEnabled
0771: * @exception UnsupportedOperationException when input method is null
0772: */
0773: public boolean isCompositionEnabled() {
0774: InputMethod inputMethod = getInputMethod();
0775:
0776: if (inputMethod == null) {
0777: throw new UnsupportedOperationException();
0778: }
0779: return inputMethod.isCompositionEnabled();
0780: }
0781:
0782: /**
0783: * @return a string with information about the current input method.
0784: * @exception UnsupportedOperationException when input method is null
0785: */
0786: public String getInputMethodInfo() {
0787: InputMethod inputMethod = getInputMethod();
0788:
0789: if (inputMethod == null) {
0790: throw new UnsupportedOperationException("Null input method");
0791: }
0792:
0793: String inputMethodInfo = null;
0794: if (inputMethod instanceof InputMethodAdapter) {
0795: // returns the information about the host native input method.
0796: inputMethodInfo = ((InputMethodAdapter) inputMethod)
0797: .getNativeInputMethodInfo();
0798: }
0799:
0800: // extracts the information from the InputMethodDescriptor
0801: // associated with the current java input method.
0802: if (inputMethodInfo == null && inputMethodLocator != null) {
0803: inputMethodInfo = inputMethodLocator.getDescriptor()
0804: .getInputMethodDisplayName(getLocale(),
0805: SunToolkit.getStartupLocale());
0806: }
0807:
0808: if (inputMethodInfo != null && !inputMethodInfo.equals("")) {
0809: return inputMethodInfo;
0810: }
0811:
0812: // do our best to return something useful.
0813: return inputMethod.toString() + "-"
0814: + inputMethod.getLocale().toString();
0815: }
0816:
0817: /**
0818: * Turns off the native IM. The native IM is diabled when
0819: * the deactive method of InputMethod is called. It is
0820: * delayed until the active method is called on a different
0821: * peer component. This method is provided to explicitly disable
0822: * the native IM.
0823: */
0824: public void disableNativeIM() {
0825: InputMethod inputMethod = getInputMethod();
0826: if (inputMethod != null
0827: && inputMethod instanceof InputMethodAdapter) {
0828: ((InputMethodAdapter) inputMethod).disableInputMethod();
0829: }
0830: }
0831:
0832: private synchronized InputMethod getInputMethod() {
0833: if (inputMethod != null) {
0834: return inputMethod;
0835: }
0836:
0837: if (inputMethodCreationFailed) {
0838: return null;
0839: }
0840:
0841: inputMethod = getInputMethodInstance();
0842: return inputMethod;
0843: }
0844:
0845: /**
0846: * Returns an instance of the input method described by
0847: * the current input method locator. This may be an input
0848: * method that was previously used and switched out of,
0849: * or a new instance. The locale, character subsets, and
0850: * input method context of the input method are set.
0851: *
0852: * The inputMethodCreationFailed field is set to true if the
0853: * instantiation failed.
0854: *
0855: * @return an InputMethod instance
0856: * @see java.awt.im.spi.InputMethod#setInputMethodContext
0857: * @see java.awt.im.spi.InputMethod#setLocale
0858: * @see java.awt.im.spi.InputMethod#setCharacterSubsets
0859: */
0860: private InputMethod getInputMethodInstance() {
0861: InputMethodLocator locator = inputMethodLocator;
0862: if (locator == null) {
0863: inputMethodCreationFailed = true;
0864: return null;
0865: }
0866:
0867: Locale locale = locator.getLocale();
0868: InputMethod inputMethodInstance = null;
0869:
0870: // see whether we have a previously used input method
0871: if (usedInputMethods != null) {
0872: inputMethodInstance = (InputMethod) usedInputMethods
0873: .remove(locator.deriveLocator(null));
0874: if (inputMethodInstance != null) {
0875: if (locale != null) {
0876: inputMethodInstance.setLocale(locale);
0877: }
0878: inputMethodInstance
0879: .setCharacterSubsets(characterSubsets);
0880: Boolean state = (Boolean) perInputMethodState
0881: .remove(inputMethodInstance);
0882: if (state != null) {
0883: enableClientWindowNotification(inputMethodInstance,
0884: state.booleanValue());
0885: }
0886: ((InputMethodContext) this )
0887: .setInputMethodSupportsBelowTheSpot((!(inputMethodInstance instanceof InputMethodAdapter))
0888: || ((InputMethodAdapter) inputMethodInstance)
0889: .supportsBelowTheSpot());
0890: return inputMethodInstance;
0891: }
0892: }
0893:
0894: // need to create new instance
0895: try {
0896: inputMethodInstance = locator.getDescriptor()
0897: .createInputMethod();
0898:
0899: if (locale != null) {
0900: inputMethodInstance.setLocale(locale);
0901: }
0902: inputMethodInstance
0903: .setInputMethodContext((InputMethodContext) this );
0904: inputMethodInstance.setCharacterSubsets(characterSubsets);
0905:
0906: } catch (Exception e) {
0907: logCreationFailed(e);
0908:
0909: // there are a number of bad things that can happen while creating
0910: // the input method. In any case, we just continue without an
0911: // input method.
0912: inputMethodCreationFailed = true;
0913:
0914: // if the instance has been created, then it means either
0915: // setLocale() or setInputMethodContext() failed.
0916: if (inputMethodInstance != null) {
0917: inputMethodInstance = null;
0918: }
0919: } catch (LinkageError e) {
0920: logCreationFailed(e);
0921:
0922: // same as above
0923: inputMethodCreationFailed = true;
0924: }
0925: ((InputMethodContext) this )
0926: .setInputMethodSupportsBelowTheSpot((!(inputMethodInstance instanceof InputMethodAdapter))
0927: || ((InputMethodAdapter) inputMethodInstance)
0928: .supportsBelowTheSpot());
0929: return inputMethodInstance;
0930: }
0931:
0932: private void logCreationFailed(Throwable throwable) {
0933: String errorTextFormat = Toolkit.getProperty(
0934: "AWT.InputMethodCreationFailed",
0935: "Could not create {0}. Reason: {1}");
0936: Object[] args = {
0937: inputMethodLocator.getDescriptor()
0938: .getInputMethodDisplayName(null,
0939: Locale.getDefault()),
0940: throwable.getLocalizedMessage() };
0941: MessageFormat mf = new MessageFormat(errorTextFormat);
0942: Logger logger = Logger.getLogger("sun.awt.im");
0943: logger.config(mf.format(args));
0944: }
0945:
0946: InputMethodLocator getInputMethodLocator() {
0947: if (inputMethod != null) {
0948: return inputMethodLocator.deriveLocator(inputMethod
0949: .getLocale());
0950: }
0951: return inputMethodLocator;
0952: }
0953:
0954: /**
0955: * @see java.awt.im.InputContext#endComposition
0956: */
0957: public synchronized void endComposition() {
0958: if (inputMethod != null) {
0959: inputMethod.endComposition();
0960: }
0961: }
0962:
0963: /**
0964: * @see java.awt.im.spi.InputMethodContext#enableClientWindowNotification
0965: */
0966: synchronized void enableClientWindowNotification(
0967: InputMethod requester, boolean enable) {
0968: // in case this request is not from the current input method,
0969: // store the request and handle it when this requesting input
0970: // method becomes the current one.
0971: if (requester != inputMethod) {
0972: if (perInputMethodState == null) {
0973: perInputMethodState = new HashMap(5);
0974: }
0975: perInputMethodState.put(requester, new Boolean(enable));
0976: return;
0977: }
0978:
0979: if (clientWindowNotificationEnabled != enable) {
0980: clientWindowLocation = null;
0981: clientWindowNotificationEnabled = enable;
0982: }
0983: if (clientWindowNotificationEnabled) {
0984: if (!addedClientWindowListeners()) {
0985: addClientWindowListeners();
0986: }
0987: if (clientWindowListened != null) {
0988: clientWindowLocation = null;
0989: notifyClientWindowChange(clientWindowListened);
0990: }
0991: } else {
0992: if (addedClientWindowListeners()) {
0993: removeClientWindowListeners();
0994: }
0995: }
0996: }
0997:
0998: private synchronized void notifyClientWindowChange(Window window) {
0999: if (inputMethod == null) {
1000: return;
1001: }
1002:
1003: // if the window is invisible or iconified, send null to the input method.
1004: if (!window.isVisible()
1005: || ((window instanceof Frame) && ((Frame) window)
1006: .getState() == Frame.ICONIFIED)) {
1007: clientWindowLocation = null;
1008: inputMethod.notifyClientWindowChange(null);
1009: return;
1010: }
1011: Rectangle location = window.getBounds();
1012: if (clientWindowLocation == null
1013: || !clientWindowLocation.equals(location)) {
1014: clientWindowLocation = location;
1015: inputMethod.notifyClientWindowChange(clientWindowLocation);
1016: }
1017: }
1018:
1019: private synchronized void addClientWindowListeners() {
1020: Component client = getClientComponent();
1021: if (client == null) {
1022: return;
1023: }
1024: Window window = getComponentWindow(client);
1025: if (window == null) {
1026: return;
1027: }
1028: window.addComponentListener(this );
1029: window.addWindowListener(this );
1030: clientWindowListened = window;
1031: }
1032:
1033: private synchronized void removeClientWindowListeners() {
1034: clientWindowListened.removeComponentListener(this );
1035: clientWindowListened.removeWindowListener(this );
1036: clientWindowListened = null;
1037: }
1038:
1039: /**
1040: * Returns true if listeners have been set up for client window
1041: * change notification.
1042: */
1043: private boolean addedClientWindowListeners() {
1044: return clientWindowListened != null;
1045: }
1046:
1047: /*
1048: * ComponentListener and WindowListener implementation
1049: */
1050: public void componentResized(ComponentEvent e) {
1051: notifyClientWindowChange((Window) e.getComponent());
1052: }
1053:
1054: public void componentMoved(ComponentEvent e) {
1055: notifyClientWindowChange((Window) e.getComponent());
1056: }
1057:
1058: public void componentShown(ComponentEvent e) {
1059: notifyClientWindowChange((Window) e.getComponent());
1060: }
1061:
1062: public void componentHidden(ComponentEvent e) {
1063: notifyClientWindowChange((Window) e.getComponent());
1064: }
1065:
1066: public void windowOpened(WindowEvent e) {
1067: }
1068:
1069: public void windowClosing(WindowEvent e) {
1070: }
1071:
1072: public void windowClosed(WindowEvent e) {
1073: }
1074:
1075: public void windowIconified(WindowEvent e) {
1076: notifyClientWindowChange(e.getWindow());
1077: }
1078:
1079: public void windowDeiconified(WindowEvent e) {
1080: notifyClientWindowChange(e.getWindow());
1081: }
1082:
1083: public void windowActivated(WindowEvent e) {
1084: }
1085:
1086: public void windowDeactivated(WindowEvent e) {
1087: }
1088:
1089: /**
1090: * Initializes the input method selection key definition in preference trees
1091: */
1092: private void initializeInputMethodSelectionKey() {
1093: AccessController.doPrivileged(new PrivilegedAction() {
1094: public Object run() {
1095: // Look in user's tree
1096: Preferences root = Preferences.userRoot();
1097: inputMethodSelectionKey = getInputMethodSelectionKeyStroke(root);
1098:
1099: if (inputMethodSelectionKey == null) {
1100: // Look in system's tree
1101: root = Preferences.systemRoot();
1102: inputMethodSelectionKey = getInputMethodSelectionKeyStroke(root);
1103: }
1104: return null;
1105: }
1106: });
1107: }
1108:
1109: private AWTKeyStroke getInputMethodSelectionKeyStroke(
1110: Preferences root) {
1111: try {
1112: if (root.nodeExists(inputMethodSelectionKeyPath)) {
1113: Preferences node = root
1114: .node(inputMethodSelectionKeyPath);
1115: int keyCode = node.getInt(
1116: inputMethodSelectionKeyCodeName,
1117: KeyEvent.VK_UNDEFINED);
1118: if (keyCode != KeyEvent.VK_UNDEFINED) {
1119: int modifiers = node.getInt(
1120: inputMethodSelectionKeyModifiersName, 0);
1121: return AWTKeyStroke.getAWTKeyStroke(keyCode,
1122: modifiers);
1123: }
1124: }
1125: } catch (BackingStoreException bse) {
1126: }
1127:
1128: return null;
1129: }
1130: }
|