Home » openjdk-7 » sun » awt » im » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.awt.im;
   27   
   28   import java.awt.AWTEvent;
   29   import java.awt.AWTKeyStroke;
   30   import java.awt.Component;
   31   import java.awt.EventQueue;
   32   import java.awt.Frame;
   33   import java.awt.Rectangle;
   34   import java.awt.Toolkit;
   35   import java.awt.Window;
   36   import java.awt.event.ComponentEvent;
   37   import java.awt.event.ComponentListener;
   38   import java.awt.event.FocusEvent;
   39   import java.awt.event.InputEvent;
   40   import java.awt.event.InputMethodEvent;
   41   import java.awt.event.KeyEvent;
   42   import java.awt.event.WindowEvent;
   43   import java.awt.event.WindowListener;
   44   import java.awt.im.InputMethodRequests;
   45   import java.awt.im.spi.InputMethod;
   46   import java.lang.Character.Subset;
   47   import java.security.AccessController;
   48   import java.security.PrivilegedAction;
   49   import java.text.MessageFormat;
   50   import java.util.HashMap;
   51   import java.util.Iterator;
   52   import java.util.Locale;
   53   import java.util.prefs.BackingStoreException;
   54   import java.util.prefs.Preferences;
   55   import sun.util.logging.PlatformLogger;
   56   import sun.awt.SunToolkit;
   57   
   58   /**
   59    * This InputContext class contains parts of the implementation of
   60    * java.text.im.InputContext. These parts have been moved
   61    * here to avoid exposing protected members that are needed by the
   62    * subclass InputMethodContext.
   63    *
   64    * @see java.awt.im.InputContext
   65    * @author JavaSoft Asia/Pacific
   66    */
   67   
   68   public class InputContext extends java.awt.im.InputContext
   69                             implements ComponentListener, WindowListener {
   70       private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.im.InputContext");
   71       // The current input method is represented by two objects:
   72       // a locator is used to keep information about the selected
   73       // input method and locale until we actually need a real input
   74       // method; only then the input method itself is created.
   75       // Once there is an input method, the input method's locale
   76       // takes precedence over locale information in the locator.
   77       private InputMethodLocator inputMethodLocator;
   78       private InputMethod inputMethod;
   79       private boolean inputMethodCreationFailed;
   80   
   81       // holding bin for previously used input method instances, but not the current one
   82       private HashMap usedInputMethods;
   83   
   84       // the current client component is kept until the user focusses on a different
   85       // client component served by the same input context. When that happens, we call
   86       // endComposition so that text doesn't jump from one component to another.
   87       private Component currentClientComponent;
   88       private Component awtFocussedComponent;
   89       private boolean   isInputMethodActive;
   90       private Subset[]  characterSubsets = null;
   91   
   92       // true if composition area has been set to invisible when focus was lost
   93       private boolean compositionAreaHidden = false;
   94   
   95       // The input context for whose input method we may have to call hideWindows
   96       private static InputContext inputMethodWindowContext;
   97   
   98       // Previously active input method to decide whether we need to call
   99       // InputMethodAdapter.stopListening() on activateInputMethod()
  100       private static InputMethod previousInputMethod = null;
  101   
  102       // true if the current input method requires client window change notification
  103       private boolean clientWindowNotificationEnabled = false;
  104       // client window to which this input context is listening
  105       private Window clientWindowListened;
  106       // cache location notification
  107       private Rectangle clientWindowLocation = null;
  108       // holding the state of clientWindowNotificationEnabled of only non-current input methods
  109       private HashMap perInputMethodState;
  110   
  111       // Input Method selection hot key stuff
  112       private static AWTKeyStroke inputMethodSelectionKey;
  113       private static boolean inputMethodSelectionKeyInitialized = false;
  114       private static final String inputMethodSelectionKeyPath = "/java/awt/im/selectionKey";
  115       private static final String inputMethodSelectionKeyCodeName = "keyCode";
  116       private static final String inputMethodSelectionKeyModifiersName = "modifiers";
  117   
  118       /**
  119        * Constructs an InputContext.
  120        */
  121       protected InputContext() {
  122           InputMethodManager imm = InputMethodManager.getInstance();
  123           synchronized (InputContext.class) {
  124               if (!inputMethodSelectionKeyInitialized) {
  125                   inputMethodSelectionKeyInitialized = true;
  126                   if (imm.hasMultipleInputMethods()) {
  127                       initializeInputMethodSelectionKey();
  128                   }
  129               }
  130           }
  131           selectInputMethod(imm.getDefaultKeyboardLocale());
  132       }
  133   
  134       /**
  135        * @see java.awt.im.InputContext#selectInputMethod
  136        * @exception NullPointerException when the locale is null.
  137        */
  138       public synchronized boolean selectInputMethod(Locale locale) {
  139           if (locale == null) {
  140               throw new NullPointerException();
  141           }
  142   
  143           // see whether the current input method supports the locale
  144           if (inputMethod != null) {
  145               if (inputMethod.setLocale(locale)) {
  146                   return true;
  147               }
  148           } else if (inputMethodLocator != null) {
  149               // This is not 100% correct, since the input method
  150               // may support the locale without advertising it.
  151               // But before we try instantiations and setLocale,
  152               // we look for an input method that's more confident.
  153               if (inputMethodLocator.isLocaleAvailable(locale)) {
  154                   inputMethodLocator = inputMethodLocator.deriveLocator(locale);
  155                   return true;
  156               }
  157           }
  158   
  159           // see whether there's some other input method that supports the locale
  160           InputMethodLocator newLocator = InputMethodManager.getInstance().findInputMethod(locale);
  161           if (newLocator != null) {
  162               changeInputMethod(newLocator);
  163               return true;
  164           }
  165   
  166           // make one last desperate effort with the current input method
  167           // ??? is this good? This is pretty high cost for something that's likely to fail.
  168           if (inputMethod == null && inputMethodLocator != null) {
  169               inputMethod = getInputMethod();
  170               if (inputMethod != null) {
  171                   return inputMethod.setLocale(locale);
  172               }
  173           }
  174           return false;
  175       }
  176   
  177       /**
  178        * @see java.awt.im.InputContext#getLocale
  179        */
  180       public Locale getLocale() {
  181           if (inputMethod != null) {
  182               return inputMethod.getLocale();
  183           } else if (inputMethodLocator != null) {
  184               return inputMethodLocator.getLocale();
  185           } else {
  186               return null;
  187           }
  188       }
  189   
  190       /**
  191        * @see java.awt.im.InputContext#setCharacterSubsets
  192        */
  193       public void setCharacterSubsets(Subset[] subsets) {
  194           if (subsets == null) {
  195               characterSubsets = null;
  196           } else {
  197               characterSubsets = new Subset[subsets.length];
  198               System.arraycopy(subsets, 0,
  199                                characterSubsets, 0, characterSubsets.length);
  200           }
  201           if (inputMethod != null) {
  202               inputMethod.setCharacterSubsets(subsets);
  203           }
  204       }
  205   
  206       /**
  207        * @see java.awt.im.InputContext#reconvert
  208        * @since 1.3
  209        * @exception UnsupportedOperationException when input method is null
  210        */
  211       public synchronized void reconvert() {
  212           InputMethod inputMethod = getInputMethod();
  213           if (inputMethod == null) {
  214               throw new UnsupportedOperationException();
  215           }
  216           inputMethod.reconvert();
  217       }
  218   
  219       /**
  220        * @see java.awt.im.InputContext#dispatchEvent
  221        */
  222       public void dispatchEvent(AWTEvent event) {
  223   
  224           if (event instanceof InputMethodEvent) {
  225               return;
  226           }
  227   
  228           // Ignore focus events that relate to the InputMethodWindow of this context.
  229           // This is a workaround.  Should be removed after 4452384 is fixed.
  230           if (event instanceof FocusEvent) {
  231               Component opposite = ((FocusEvent)event).getOppositeComponent();
  232               if ((opposite != null) &&
  233                   (getComponentWindow(opposite) instanceof InputMethodWindow) &&
  234                   (opposite.getInputContext() == this)) {
  235                   return;
  236               }
  237           }
  238   
  239           InputMethod inputMethod = getInputMethod();
  240           int id = event.getID();
  241   
  242           switch (id) {
  243           case FocusEvent.FOCUS_GAINED:
  244               focusGained((Component) event.getSource());
  245               break;
  246   
  247           case FocusEvent.FOCUS_LOST:
  248               focusLost((Component) event.getSource(), ((FocusEvent) event).isTemporary());
  249               break;
  250   
  251           case KeyEvent.KEY_PRESSED:
  252               if (checkInputMethodSelectionKey((KeyEvent)event)) {
  253                   // pop up the input method selection menu
  254                   InputMethodManager.getInstance().notifyChangeRequestByHotKey((Component)event.getSource());
  255                   break;
  256               }
  257   
  258               // fall through
  259   
  260           default:
  261               if ((inputMethod != null) && (event instanceof InputEvent)) {
  262                   inputMethod.dispatchEvent(event);
  263               }
  264           }
  265       }
  266   
  267       /**
  268        * Handles focus gained events for any component that's using
  269        * this input context.
  270        * These events are generated by AWT when the keyboard focus
  271        * moves to a component.
  272        * Besides actual client components, the source components
  273        * may also be the composition area or any component in an
  274        * input method window.
  275        * <p>
  276        * When handling the focus event for a client component, this
  277        * method checks whether the input context was previously
  278        * active for a different client component, and if so, calls
  279        * endComposition for the previous client component.
  280        *
  281        * @param source the component gaining the focus
  282        */
  283       private void focusGained(Component source) {
  284   
  285           /*
  286            * NOTE: When a Container is removing its Component which
  287            * invokes this.removeNotify(), the Container has the global
  288            * Component lock. It is possible to happen that an
  289            * application thread is calling this.removeNotify() while an
  290            * AWT event queue thread is dispatching a focus event via
  291            * this.dispatchEvent(). If an input method uses AWT
  292            * components (e.g., IIIMP status window), it causes deadlock,
  293            * for example, Component.show()/hide() in this situation
  294            * because hide/show tried to obtain the lock.  Therefore,
  295            * it's necessary to obtain the global Component lock before
  296            * activating or deactivating an input method.
  297            */
  298           synchronized (source.getTreeLock()) {
  299               synchronized (this) {
  300                   if ("sun.awt.im.CompositionArea".equals(source.getClass().getName())) {
  301                       // no special handling for this one
  302                   } else if (getComponentWindow(source) instanceof InputMethodWindow) {
  303                       // no special handling for this one either
  304                   } else {
  305                       if (!source.isDisplayable()) {
  306                           // Component is being disposed
  307                           return;
  308                       }
  309   
  310                       // Focus went to a real client component.
  311                       // Check whether we're switching between client components
  312                       // that share an input context. We can't do that earlier
  313                       // than here because we don't want to end composition
  314                       // until we really know we're switching to a different component
  315                       if (inputMethod != null) {
  316                           if (currentClientComponent != null && currentClientComponent != source) {
  317                               if (!isInputMethodActive) {
  318                                   activateInputMethod(false);
  319                               }
  320                               endComposition();
  321                               deactivateInputMethod(false);
  322                           }
  323                       }
  324   
  325                       currentClientComponent = source;
  326                   }
  327   
  328                   awtFocussedComponent = source;
  329                   if (inputMethod instanceof InputMethodAdapter) {
  330                       ((InputMethodAdapter) inputMethod).setAWTFocussedComponent(source);
  331                   }
  332   
  333                   // it's possible that the input method is still active because
  334                   // we suppressed a deactivate cause by an input method window
  335                   // coming up
  336                   if (!isInputMethodActive) {
  337                       activateInputMethod(true);
  338                   }
  339   
  340   
  341                   // If the client component is an active client with the below-the-spot
  342                   // input style, then make the composition window undecorated without a title bar.
  343                   InputMethodContext inputContext = ((InputMethodContext)this);
  344                   if (!inputContext.isCompositionAreaVisible()) {
  345                         InputMethodRequests req = source.getInputMethodRequests();
  346                         if (req != null && inputContext.useBelowTheSpotInput()) {
  347                             inputContext.setCompositionAreaUndecorated(true);
  348                         } else {
  349                             inputContext.setCompositionAreaUndecorated(false);
  350                         }
  351                   }
  352                   // restores the composition area if it was set to invisible
  353                   // when focus got lost
  354                   if (compositionAreaHidden == true) {
  355                       ((InputMethodContext)this).setCompositionAreaVisible(true);
  356                       compositionAreaHidden = false;
  357                   }
  358               }
  359           }
  360       }
  361   
  362       /**
  363        * Activates the current input method of this input context, and grabs
  364        * the composition area for use by this input context.
  365        * If updateCompositionArea is true, the text in the composition area
  366        * is updated (set to false if the text is going to change immediately
  367        * to avoid screen flicker).
  368        */
  369       private void activateInputMethod(boolean updateCompositionArea) {
  370           // call hideWindows() if this input context uses a different
  371           // input method than the previously activated one
  372           if (inputMethodWindowContext != null && inputMethodWindowContext != this &&
  373                   inputMethodWindowContext.inputMethodLocator != null &&
  374                   !inputMethodWindowContext.inputMethodLocator.sameInputMethod(inputMethodLocator) &&
  375                   inputMethodWindowContext.inputMethod != null) {
  376               inputMethodWindowContext.inputMethod.hideWindows();
  377           }
  378           inputMethodWindowContext = this;
  379   
  380           if (inputMethod != null) {
  381               if (previousInputMethod != inputMethod &&
  382                       previousInputMethod instanceof InputMethodAdapter) {
  383                   // let the host adapter pass through the input events for the
  384                   // new input method
  385                   ((InputMethodAdapter) previousInputMethod).stopListening();
  386               }
  387               previousInputMethod = null;
  388   
  389               if (log.isLoggable(PlatformLogger.FINE)) log.fine("Current client component " + currentClientComponent);
  390               if (inputMethod instanceof InputMethodAdapter) {
  391                   ((InputMethodAdapter) inputMethod).setClientComponent(currentClientComponent);
  392               }
  393               inputMethod.activate();
  394               isInputMethodActive = true;
  395   
  396               if (perInputMethodState != null) {
  397                   Boolean state = (Boolean) perInputMethodState.remove(inputMethod);
  398                   if (state != null) {
  399                       clientWindowNotificationEnabled = state.booleanValue();
  400                   }
  401               }
  402               if (clientWindowNotificationEnabled) {
  403                   if (!addedClientWindowListeners()) {
  404                       addClientWindowListeners();
  405                   }
  406                   synchronized(this) {
  407                       if (clientWindowListened != null) {
  408                           notifyClientWindowChange(clientWindowListened);
  409                       }
  410                   }
  411               } else {
  412                   if (addedClientWindowListeners()) {
  413                       removeClientWindowListeners();
  414                   }
  415               }
  416           }
  417           InputMethodManager.getInstance().setInputContext(this);
  418   
  419           ((InputMethodContext) this).grabCompositionArea(updateCompositionArea);
  420       }
  421   
  422       static Window getComponentWindow(Component component) {
  423           while (true) {
  424               if (component == null) {
  425                   return null;
  426               } else if (component instanceof Window) {
  427                   return (Window) component;
  428               } else {
  429                   component = component.getParent();
  430               }
  431           }
  432       }
  433   
  434       /**
  435        * Handles focus lost events for any component that's using
  436        * this input context.
  437        * These events are generated by AWT when the keyboard focus
  438        * moves away from a component.
  439        * Besides actual client components, the source components
  440        * may also be the composition area or any component in an
  441        * input method window.
  442        *
  443        * @param source the component losing the focus
  444        * @isTemporary whether the focus change is temporary
  445        */
  446       private void focusLost(Component source, boolean isTemporary) {
  447   
  448           // see the note on synchronization in focusGained
  449           synchronized (source.getTreeLock()) {
  450               synchronized (this) {
  451   
  452                   // We need to suppress deactivation if removeNotify has been called earlier.
  453                   // This is indicated by isInputMethodActive == false.
  454                   if (isInputMethodActive) {
  455                       deactivateInputMethod(isTemporary);
  456                   }
  457   
  458                   awtFocussedComponent = null;
  459                   if (inputMethod instanceof InputMethodAdapter) {
  460                       ((InputMethodAdapter) inputMethod).setAWTFocussedComponent(null);
  461                   }
  462   
  463                   // hides the composition area if currently it is visible
  464                   InputMethodContext inputContext = ((InputMethodContext)this);
  465                   if (inputContext.isCompositionAreaVisible()) {
  466                       inputContext.setCompositionAreaVisible(false);
  467                       compositionAreaHidden = true;
  468                   }
  469               }
  470           }
  471       }
  472   
  473       /**
  474        * Checks the key event is the input method selection key or not.
  475        */
  476       private boolean checkInputMethodSelectionKey(KeyEvent event) {
  477           if (inputMethodSelectionKey != null) {
  478               AWTKeyStroke aKeyStroke = AWTKeyStroke.getAWTKeyStrokeForEvent(event);
  479               return inputMethodSelectionKey.equals(aKeyStroke);
  480           } else {
  481               return false;
  482           }
  483       }
  484   
  485       private void deactivateInputMethod(boolean isTemporary) {
  486           InputMethodManager.getInstance().setInputContext(null);
  487           if (inputMethod != null) {
  488               isInputMethodActive = false;
  489               inputMethod.deactivate(isTemporary);
  490               previousInputMethod = inputMethod;
  491           }
  492       }
  493   
  494       /**
  495        * Switches from the current input method to the one described by newLocator.
  496        * The current input method, if any, is asked to end composition, deactivated,
  497        * and saved for future use. The newLocator is made the current locator. If
  498        * the input context is active, an input method instance for the new locator
  499        * is obtained; otherwise this is deferred until required.
  500        */
  501       synchronized void changeInputMethod(InputMethodLocator newLocator) {
  502           // If we don't have a locator yet, this must be a new input context.
  503           // If we created a new input method here, we might get into an
  504           // infinite loop: create input method -> create some input method window ->
  505           // create new input context -> add input context to input method manager's context list ->
  506           // call changeInputMethod on it.
  507           // So, just record the locator. dispatchEvent will create the input method when needed.
  508           if (inputMethodLocator == null) {
  509               inputMethodLocator = newLocator;
  510               inputMethodCreationFailed = false;
  511               return;
  512           }
  513   
  514           // If the same input method is specified, just keep it.
  515           // Adjust the locale if necessary.
  516           if (inputMethodLocator.sameInputMethod(newLocator)) {
  517               Locale newLocale = newLocator.getLocale();
  518               if (newLocale != null && inputMethodLocator.getLocale() != newLocale) {
  519                   if (inputMethod != null) {
  520                       inputMethod.setLocale(newLocale);
  521                   }
  522                   inputMethodLocator = newLocator;
  523               }
  524               return;
  525           }
  526   
  527           // Switch out the old input method
  528           Locale savedLocale = inputMethodLocator.getLocale();
  529           boolean wasInputMethodActive = isInputMethodActive;
  530           boolean wasCompositionEnabledSupported = false;
  531           boolean wasCompositionEnabled = false;
  532           if (inputMethod != null) {
  533               try {
  534                   wasCompositionEnabled = inputMethod.isCompositionEnabled();
  535                   wasCompositionEnabledSupported = true;
  536               } catch (UnsupportedOperationException e) { }
  537   
  538               if (currentClientComponent != null) {
  539                   if (!isInputMethodActive) {
  540                       activateInputMethod(false);
  541                   }
  542                   endComposition();
  543                   deactivateInputMethod(false);
  544                   if (inputMethod instanceof InputMethodAdapter) {
  545                       ((InputMethodAdapter) inputMethod).setClientComponent(null);
  546                   }
  547               }
  548               savedLocale = inputMethod.getLocale();
  549   
  550               // keep the input method instance around for future use
  551               if (usedInputMethods == null) {
  552                   usedInputMethods = new HashMap(5);
  553               }
  554               if (perInputMethodState == null) {
  555                   perInputMethodState = new HashMap(5);
  556               }
  557               usedInputMethods.put(inputMethodLocator.deriveLocator(null), inputMethod);
  558               perInputMethodState.put(inputMethod,
  559                                       Boolean.valueOf(clientWindowNotificationEnabled));
  560               enableClientWindowNotification(inputMethod, false);
  561               if (this == inputMethodWindowContext) {
  562                   inputMethod.hideWindows();
  563                   inputMethodWindowContext = null;
  564               }
  565               inputMethodLocator = null;
  566               inputMethod = null;
  567               inputMethodCreationFailed = false;
  568           }
  569   
  570           // Switch in the new input method
  571           if (newLocator.getLocale() == null && savedLocale != null &&
  572                   newLocator.isLocaleAvailable(savedLocale)) {
  573               newLocator = newLocator.deriveLocator(savedLocale);
  574           }
  575           inputMethodLocator = newLocator;
  576           inputMethodCreationFailed = false;
  577   
  578           // activate the new input method if the old one was active
  579           if (wasInputMethodActive) {
  580               inputMethod = getInputMethodInstance();
  581               if (inputMethod instanceof InputMethodAdapter) {
  582                   ((InputMethodAdapter) inputMethod).setAWTFocussedComponent(awtFocussedComponent);
  583               }
  584               activateInputMethod(true);
  585           }
  586   
  587           // enable/disable composition if the old one supports querying enable/disable
  588           if (wasCompositionEnabledSupported) {
  589               inputMethod = getInputMethod();
  590               if (inputMethod != null) {
  591                   try {
  592                       inputMethod.setCompositionEnabled(wasCompositionEnabled);
  593                   } catch (UnsupportedOperationException e) { }
  594               }
  595           }
  596       }
  597   
  598       /**
  599        * Returns the client component.
  600        */
  601       Component getClientComponent() {
  602           return currentClientComponent;
  603       }
  604   
  605       /**
  606        * @see java.awt.im.InputContext#removeNotify
  607        * @exception NullPointerException when the component is null.
  608        */
  609       public synchronized void removeNotify(Component component) {
  610           if (component == null) {
  611               throw new NullPointerException();
  612           }
  613   
  614           if (inputMethod == null) {
  615               if (component == currentClientComponent) {
  616                   currentClientComponent = null;
  617               }
  618               return;
  619           }
  620   
  621           // We may or may not get a FOCUS_LOST event for this component,
  622           // so do the deactivation stuff here too.
  623           if (component == awtFocussedComponent) {
  624               focusLost(component, false);
  625           }
  626   
  627           if (component == currentClientComponent) {
  628               if (isInputMethodActive) {
  629                   // component wasn't the one that had the focus
  630                   deactivateInputMethod(false);
  631               }
  632               inputMethod.removeNotify();
  633               if (clientWindowNotificationEnabled && addedClientWindowListeners()) {
  634                   removeClientWindowListeners();
  635               }
  636               currentClientComponent = null;
  637               if (inputMethod instanceof InputMethodAdapter) {
  638                   ((InputMethodAdapter) inputMethod).setClientComponent(null);
  639               }
  640   
  641               // removeNotify() can be issued from a thread other than the event dispatch
  642               // thread.  In that case, avoid possible deadlock between Component.AWTTreeLock
  643               // and InputMethodContext.compositionAreaHandlerLock by releasing the composition
  644               // area on the event dispatch thread.
  645               if (EventQueue.isDispatchThread()) {
  646                   ((InputMethodContext)this).releaseCompositionArea();
  647               } else {
  648                   EventQueue.invokeLater(new Runnable() {
  649                       public void run() {
  650                           ((InputMethodContext)InputContext.this).releaseCompositionArea();
  651                       }
  652                   });
  653               }
  654           }
  655       }
  656   
  657       /**
  658        * @see java.awt.im.InputContext#dispose
  659        * @exception IllegalStateException when the currentClientComponent is not null
  660        */
  661       public synchronized void dispose() {
  662           if (currentClientComponent != null) {
  663               throw new IllegalStateException("Can't dispose InputContext while it's active");
  664           }
  665           if (inputMethod != null) {
  666               if (this == inputMethodWindowContext) {
  667                   inputMethod.hideWindows();
  668                   inputMethodWindowContext = null;
  669               }
  670               if (inputMethod == previousInputMethod) {
  671                   previousInputMethod = null;
  672               }
  673               if (clientWindowNotificationEnabled) {
  674                   if (addedClientWindowListeners()) {
  675                       removeClientWindowListeners();
  676                   }
  677                   clientWindowNotificationEnabled = false;
  678               }
  679               inputMethod.dispose();
  680   
  681               // in case the input method enabled the client window
  682               // notification in dispose(), which shouldn't happen, it
  683               // needs to be cleaned up again.
  684               if (clientWindowNotificationEnabled) {
  685                   enableClientWindowNotification(inputMethod, false);
  686               }
  687   
  688               inputMethod = null;
  689           }
  690           inputMethodLocator = null;
  691           if (usedInputMethods != null && !usedInputMethods.isEmpty()) {
  692               Iterator iterator = usedInputMethods.values().iterator();
  693               usedInputMethods = null;
  694               while (iterator.hasNext()) {
  695                   ((InputMethod) iterator.next()).dispose();
  696               }
  697           }
  698   
  699           // cleanup client window notification variables
  700           clientWindowNotificationEnabled = false;
  701           clientWindowListened = null;
  702           perInputMethodState = null;
  703       }
  704   
  705       /**
  706        * @see java.awt.im.InputContext#getInputMethodControlObject
  707        */
  708       public synchronized Object getInputMethodControlObject() {
  709           InputMethod inputMethod = getInputMethod();
  710   
  711           if (inputMethod != null) {
  712               return inputMethod.getControlObject();
  713           } else {
  714               return null;
  715           }
  716       }
  717   
  718       /**
  719        * @see java.awt.im.InputContext#setCompositionEnabled(boolean)
  720        * @exception UnsupportedOperationException when input method is null
  721        */
  722       public void setCompositionEnabled(boolean enable) {
  723           InputMethod inputMethod = getInputMethod();
  724   
  725           if (inputMethod == null) {
  726               throw new UnsupportedOperationException();
  727           }
  728           inputMethod.setCompositionEnabled(enable);
  729       }
  730   
  731       /**
  732        * @see java.awt.im.InputContext#isCompositionEnabled
  733        * @exception UnsupportedOperationException when input method is null
  734        */
  735       public boolean isCompositionEnabled() {
  736           InputMethod inputMethod = getInputMethod();
  737   
  738           if (inputMethod == null) {
  739               throw new UnsupportedOperationException();
  740           }
  741           return inputMethod.isCompositionEnabled();
  742       }
  743   
  744       /**
  745        * @return a string with information about the current input method.
  746        * @exception UnsupportedOperationException when input method is null
  747        */
  748       public String getInputMethodInfo() {
  749           InputMethod inputMethod = getInputMethod();
  750   
  751           if (inputMethod == null) {
  752               throw new UnsupportedOperationException("Null input method");
  753           }
  754   
  755           String inputMethodInfo = null;
  756           if (inputMethod instanceof InputMethodAdapter) {
  757               // returns the information about the host native input method.
  758               inputMethodInfo = ((InputMethodAdapter)inputMethod).
  759                   getNativeInputMethodInfo();
  760           }
  761   
  762           // extracts the information from the InputMethodDescriptor
  763           // associated with the current java input method.
  764           if (inputMethodInfo == null && inputMethodLocator != null) {
  765               inputMethodInfo = inputMethodLocator.getDescriptor().
  766                   getInputMethodDisplayName(getLocale(), SunToolkit.
  767                                             getStartupLocale());
  768           }
  769   
  770           if (inputMethodInfo != null && !inputMethodInfo.equals("")) {
  771               return inputMethodInfo;
  772           }
  773   
  774           // do our best to return something useful.
  775           return inputMethod.toString() + "-" + inputMethod.getLocale().toString();
  776       }
  777   
  778       /**
  779        * Turns off the native IM. The native IM is diabled when
  780        * the deactive method of InputMethod is called. It is
  781        * delayed until the active method is called on a different
  782        * peer component. This method is provided to explicitly disable
  783        * the native IM.
  784        */
  785       public void disableNativeIM() {
  786           InputMethod inputMethod = getInputMethod();
  787           if (inputMethod != null && inputMethod instanceof InputMethodAdapter) {
  788               ((InputMethodAdapter)inputMethod).disableInputMethod();
  789           }
  790       }
  791   
  792   
  793       private synchronized InputMethod getInputMethod() {
  794           if (inputMethod != null) {
  795               return inputMethod;
  796           }
  797   
  798           if (inputMethodCreationFailed) {
  799               return null;
  800           }
  801   
  802           inputMethod = getInputMethodInstance();
  803           return inputMethod;
  804       }
  805   
  806       /**
  807        * Returns an instance of the input method described by
  808        * the current input method locator. This may be an input
  809        * method that was previously used and switched out of,
  810        * or a new instance. The locale, character subsets, and
  811        * input method context of the input method are set.
  812        *
  813        * The inputMethodCreationFailed field is set to true if the
  814        * instantiation failed.
  815        *
  816        * @return an InputMethod instance
  817        * @see java.awt.im.spi.InputMethod#setInputMethodContext
  818        * @see java.awt.im.spi.InputMethod#setLocale
  819        * @see java.awt.im.spi.InputMethod#setCharacterSubsets
  820        */
  821       private InputMethod getInputMethodInstance() {
  822           InputMethodLocator locator = inputMethodLocator;
  823           if (locator == null) {
  824               inputMethodCreationFailed = true;
  825               return null;
  826           }
  827   
  828           Locale locale = locator.getLocale();
  829           InputMethod inputMethodInstance = null;
  830   
  831           // see whether we have a previously used input method
  832           if (usedInputMethods != null) {
  833               inputMethodInstance = (InputMethod) usedInputMethods.remove(locator.deriveLocator(null));
  834               if (inputMethodInstance != null) {
  835                   if (locale != null) {
  836                       inputMethodInstance.setLocale(locale);
  837                   }
  838                   inputMethodInstance.setCharacterSubsets(characterSubsets);
  839                   Boolean state = (Boolean) perInputMethodState.remove(inputMethodInstance);
  840                   if (state != null) {
  841                       enableClientWindowNotification(inputMethodInstance, state.booleanValue());
  842                   }
  843                   ((InputMethodContext) this).setInputMethodSupportsBelowTheSpot(
  844                           (!(inputMethodInstance instanceof InputMethodAdapter)) ||
  845                           ((InputMethodAdapter) inputMethodInstance).supportsBelowTheSpot());
  846                   return inputMethodInstance;
  847               }
  848           }
  849   
  850           // need to create new instance
  851           try {
  852               inputMethodInstance = locator.getDescriptor().createInputMethod();
  853   
  854               if (locale != null) {
  855                   inputMethodInstance.setLocale(locale);
  856               }
  857               inputMethodInstance.setInputMethodContext((InputMethodContext) this);
  858               inputMethodInstance.setCharacterSubsets(characterSubsets);
  859   
  860           } catch (Exception e) {
  861               logCreationFailed(e);
  862   
  863               // there are a number of bad things that can happen while creating
  864               // the input method. In any case, we just continue without an
  865               // input method.
  866               inputMethodCreationFailed = true;
  867   
  868               // if the instance has been created, then it means either
  869               // setLocale() or setInputMethodContext() failed.
  870               if (inputMethodInstance != null) {
  871                   inputMethodInstance = null;
  872               }
  873           } catch (LinkageError e) {
  874               logCreationFailed(e);
  875   
  876               // same as above
  877               inputMethodCreationFailed = true;
  878           }
  879           ((InputMethodContext) this).setInputMethodSupportsBelowTheSpot(
  880                   (!(inputMethodInstance instanceof InputMethodAdapter)) ||
  881                   ((InputMethodAdapter) inputMethodInstance).supportsBelowTheSpot());
  882           return inputMethodInstance;
  883       }
  884   
  885       private void logCreationFailed(Throwable throwable) {
  886           String errorTextFormat = Toolkit.getProperty("AWT.InputMethodCreationFailed",
  887                                                        "Could not create {0}. Reason: {1}");
  888           Object[] args =
  889               {inputMethodLocator.getDescriptor().getInputMethodDisplayName(null, Locale.getDefault()),
  890                throwable.getLocalizedMessage()};
  891           MessageFormat mf = new MessageFormat(errorTextFormat);
  892           PlatformLogger logger = PlatformLogger.getLogger("sun.awt.im");
  893           logger.config(mf.format(args));
  894       }
  895   
  896       InputMethodLocator getInputMethodLocator() {
  897           if (inputMethod != null) {
  898               return inputMethodLocator.deriveLocator(inputMethod.getLocale());
  899           }
  900           return inputMethodLocator;
  901       }
  902   
  903       /**
  904        * @see java.awt.im.InputContext#endComposition
  905        */
  906       public synchronized void endComposition() {
  907           if (inputMethod != null) {
  908               inputMethod.endComposition();
  909           }
  910       }
  911   
  912       /**
  913        * @see java.awt.im.spi.InputMethodContext#enableClientWindowNotification
  914        */
  915       synchronized void enableClientWindowNotification(InputMethod requester,
  916                                                        boolean enable) {
  917           // in case this request is not from the current input method,
  918           // store the request and handle it when this requesting input
  919           // method becomes the current one.
  920           if (requester != inputMethod) {
  921               if (perInputMethodState == null) {
  922                   perInputMethodState = new HashMap(5);
  923               }
  924               perInputMethodState.put(requester, Boolean.valueOf(enable));
  925               return;
  926           }
  927   
  928           if (clientWindowNotificationEnabled != enable) {
  929               clientWindowLocation = null;
  930               clientWindowNotificationEnabled = enable;
  931           }
  932           if (clientWindowNotificationEnabled) {
  933               if (!addedClientWindowListeners()) {
  934                   addClientWindowListeners();
  935               }
  936               if (clientWindowListened != null) {
  937                   clientWindowLocation = null;
  938                   notifyClientWindowChange(clientWindowListened);
  939               }
  940           } else {
  941               if (addedClientWindowListeners()) {
  942                   removeClientWindowListeners();
  943               }
  944           }
  945       }
  946   
  947       private synchronized void notifyClientWindowChange(Window window) {
  948           if (inputMethod == null) {
  949               return;
  950           }
  951   
  952           // if the window is invisible or iconified, send null to the input method.
  953           if (!window.isVisible() ||
  954               ((window instanceof Frame) && ((Frame)window).getState() == Frame.ICONIFIED)) {
  955               clientWindowLocation = null;
  956               inputMethod.notifyClientWindowChange(null);
  957               return;
  958           }
  959           Rectangle location = window.getBounds();
  960           if (clientWindowLocation == null || !clientWindowLocation.equals(location)) {
  961               clientWindowLocation = location;
  962               inputMethod.notifyClientWindowChange(clientWindowLocation);
  963           }
  964       }
  965   
  966       private synchronized void addClientWindowListeners() {
  967           Component client = getClientComponent();
  968           if (client == null) {
  969               return;
  970           }
  971           Window window = getComponentWindow(client);
  972           if (window == null) {
  973               return;
  974           }
  975           window.addComponentListener(this);
  976           window.addWindowListener(this);
  977           clientWindowListened = window;
  978       }
  979   
  980       private synchronized void removeClientWindowListeners() {
  981           clientWindowListened.removeComponentListener(this);
  982           clientWindowListened.removeWindowListener(this);
  983           clientWindowListened = null;
  984       }
  985   
  986       /**
  987        * Returns true if listeners have been set up for client window
  988        * change notification.
  989        */
  990       private boolean addedClientWindowListeners() {
  991           return clientWindowListened != null;
  992       }
  993   
  994       /*
  995        * ComponentListener and WindowListener implementation
  996        */
  997       public void componentResized(ComponentEvent e) {
  998           notifyClientWindowChange((Window)e.getComponent());
  999       }
 1000   
 1001       public void componentMoved(ComponentEvent e) {
 1002           notifyClientWindowChange((Window)e.getComponent());
 1003       }
 1004   
 1005       public void componentShown(ComponentEvent e) {
 1006           notifyClientWindowChange((Window)e.getComponent());
 1007       }
 1008   
 1009       public void componentHidden(ComponentEvent e) {
 1010           notifyClientWindowChange((Window)e.getComponent());
 1011       }
 1012   
 1013       public void windowOpened(WindowEvent e) {}
 1014       public void windowClosing(WindowEvent e) {}
 1015       public void windowClosed(WindowEvent e) {}
 1016   
 1017       public void windowIconified(WindowEvent e) {
 1018           notifyClientWindowChange(e.getWindow());
 1019       }
 1020   
 1021       public void windowDeiconified(WindowEvent e) {
 1022           notifyClientWindowChange(e.getWindow());
 1023       }
 1024   
 1025       public void windowActivated(WindowEvent e) {}
 1026       public void windowDeactivated(WindowEvent e) {}
 1027   
 1028       /**
 1029        * Initializes the input method selection key definition in preference trees
 1030        */
 1031       private void initializeInputMethodSelectionKey() {
 1032           AccessController.doPrivileged(new PrivilegedAction() {
 1033               public Object run() {
 1034                   // Look in user's tree
 1035                   Preferences root = Preferences.userRoot();
 1036                   inputMethodSelectionKey = getInputMethodSelectionKeyStroke(root);
 1037   
 1038                   if (inputMethodSelectionKey == null) {
 1039                       // Look in system's tree
 1040                       root = Preferences.systemRoot();
 1041                       inputMethodSelectionKey = getInputMethodSelectionKeyStroke(root);
 1042                   }
 1043                   return null;
 1044               }
 1045           });
 1046       }
 1047   
 1048       private AWTKeyStroke getInputMethodSelectionKeyStroke(Preferences root) {
 1049           try {
 1050               if (root.nodeExists(inputMethodSelectionKeyPath)) {
 1051                   Preferences node = root.node(inputMethodSelectionKeyPath);
 1052                   int keyCode = node.getInt(inputMethodSelectionKeyCodeName, KeyEvent.VK_UNDEFINED);
 1053                   if (keyCode != KeyEvent.VK_UNDEFINED) {
 1054                       int modifiers = node.getInt(inputMethodSelectionKeyModifiersName, 0);
 1055                       return AWTKeyStroke.getAWTKeyStroke(keyCode, modifiers);
 1056                   }
 1057               }
 1058           } catch (BackingStoreException bse) {
 1059           }
 1060   
 1061           return null;
 1062       }
 1063   }

Home » openjdk-7 » sun » awt » im » [javadoc | source]