src/java.desktop/aix/classes/sun/awt/X11InputMethod.java
changeset 50350 668463f93ec0
parent 47216 71c04702a3d5
child 52248 2e330da7cbf4
equal deleted inserted replaced
50349:b3a654c6fbcc 50350:668463f93ec0
       
     1 /*
       
     2  * Copyright (c) 1997, 2018, 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;
       
    27 
       
    28 import java.awt.AWTException;
       
    29 import java.awt.EventQueue;
       
    30 import java.awt.event.InputMethodEvent;
       
    31 import java.awt.font.TextAttribute;
       
    32 import java.awt.font.TextHitInfo;
       
    33 import java.awt.peer.ComponentPeer;
       
    34 import java.text.AttributedString;
       
    35 
       
    36 import sun.util.logging.PlatformLogger;
       
    37 
       
    38 /**
       
    39  * Input Method Adapter for XIM for AIX
       
    40  *
       
    41  * @author JavaSoft International
       
    42  */
       
    43 public abstract class X11InputMethod extends X11InputMethodBase {
       
    44 
       
    45     // to keep the instance of activating if IM resumed
       
    46     static protected X11InputMethod activatedInstance = null;
       
    47 
       
    48     /**
       
    49      * Constructs an X11InputMethod instance. It initializes the XIM
       
    50      * environment if it's not done yet.
       
    51      *
       
    52      * @exception AWTException if XOpenIM() failed.
       
    53      */
       
    54     public X11InputMethod() throws AWTException {
       
    55         super();
       
    56     }
       
    57 
       
    58     /**
       
    59      * Reset the composition state to the current composition state.
       
    60      */
       
    61     protected void resetCompositionState() {
       
    62         if (compositionEnableSupported && haveActiveClient()) {
       
    63             try {
       
    64                 /* Restore the composition mode to the last saved composition
       
    65                    mode. */
       
    66                 setCompositionEnabled(savedCompositionState);
       
    67             } catch (UnsupportedOperationException e) {
       
    68                 compositionEnableSupported = false;
       
    69             }
       
    70         }
       
    71     }
       
    72 
       
    73     /**
       
    74      * Activate input method.
       
    75      */
       
    76     public synchronized void activate() {
       
    77         activatedInstance = this;
       
    78         clientComponentWindow = getClientComponentWindow();
       
    79         if (clientComponentWindow == null)
       
    80             return;
       
    81 
       
    82         if (lastXICFocussedComponent != null) {
       
    83             if (log.isLoggable(PlatformLogger.Level.FINE)) {
       
    84                 log.fine("XICFocused {0}, AWTFocused {1}",
       
    85                          lastXICFocussedComponent, awtFocussedComponent);
       
    86             }
       
    87             if (lastXICFocussedComponent != awtFocussedComponent) {
       
    88                 ComponentPeer lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent);
       
    89                 if (lastXICFocussedComponentPeer != null) {
       
    90                     setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive);
       
    91                 }
       
    92             }
       
    93             lastXICFocussedComponent = null;
       
    94         }
       
    95 
       
    96         if (pData == 0) {
       
    97             if (!createXIC()) {
       
    98                 return;
       
    99             }
       
   100             disposed = false;
       
   101         }
       
   102 
       
   103         /*  reset input context if necessary and set the XIC focus
       
   104         */
       
   105         resetXICifneeded();
       
   106         ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);
       
   107         setStatusAreaVisible(true, pData);
       
   108 
       
   109         if (awtFocussedComponentPeer != null) {
       
   110             setXICFocus(awtFocussedComponentPeer, true, haveActiveClient());
       
   111         }
       
   112         lastXICFocussedComponent = awtFocussedComponent;
       
   113         isLastXICActive = haveActiveClient();
       
   114         isActive = true;
       
   115         if (savedCompositionState) {
       
   116             resetCompositionState();
       
   117         }
       
   118     }
       
   119 
       
   120     /**
       
   121      * Deactivate input method.
       
   122      */
       
   123     public synchronized void deactivate(boolean isTemporary) {
       
   124         boolean   isAc =  haveActiveClient();
       
   125         /* Usually as the client component, let's call it component A,
       
   126            loses the focus, this method is called. Then when another client
       
   127            component, let's call it component B,  gets the focus, activate is first called on
       
   128            the previous focused compoent which is A, then endComposition is called on A,
       
   129            deactivate is called on A again. And finally activate is called on the newly
       
   130            focused component B. Here is the call sequence.
       
   131 
       
   132            A loses focus               B gains focus
       
   133            -------------> deactivate A -------------> activate A -> endComposition A ->
       
   134            deactivate A -> activate B ----....
       
   135 
       
   136            So in order to carry the composition mode across the components sharing the same
       
   137            input context, we save it when deactivate is called so that when activate is
       
   138            called, it can be restored correctly till activate is called on the newly focused
       
   139            component. (See also sun/awt/im/InputContext and bug 6184471).
       
   140            Last note, getCompositionState should be called before setXICFocus since
       
   141            setXICFocus here sets the XIC to 0.
       
   142         */
       
   143         activatedInstance = null;
       
   144         savedCompositionState = getCompositionState();
       
   145 
       
   146         if (isTemporary) {
       
   147             //turn the status window off...
       
   148             turnoffStatusWindow();
       
   149             /* Delay resetting the XIC focus until activate is called and the newly
       
   150              * Focused component has a different peer as the last focused component.
       
   151              */
       
   152             lastXICFocussedComponent = awtFocussedComponent;
       
   153         } else {
       
   154             if (awtFocussedComponent != null ) {
       
   155                 ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);
       
   156                 if (awtFocussedComponentPeer != null) {
       
   157                     setXICFocus(awtFocussedComponentPeer, false, isAc);
       
   158                 }
       
   159             }
       
   160             lastXICFocussedComponent = null;
       
   161         }
       
   162 
       
   163         isLastXICActive = isAc;
       
   164         isLastTemporary = isTemporary;
       
   165         isActive = false;
       
   166         setStatusAreaVisible(false, pData);
       
   167     }
       
   168 
       
   169     // implements java.awt.im.spi.InputMethod.hideWindows
       
   170     public void hideWindows() {
       
   171         if (pData != 0) {
       
   172             setStatusAreaVisible(false, pData);
       
   173             turnoffStatusWindow();
       
   174         }
       
   175     }
       
   176 
       
   177     /**
       
   178      * Updates composed text with XIM preedit information and
       
   179      * posts composed text to the awt event queue. The args of
       
   180      * this method correspond to the XIM preedit callback
       
   181      * information. The XIM highlight attributes are translated via
       
   182      * fixed mapping (i.e., independent from any underlying input
       
   183      * method engine). This method is invoked in the AWT Toolkit
       
   184      * (X event loop) thread context and thus inside the AWT Lock.
       
   185      */
       
   186     // NOTE: This method may be called by privileged threads.
       
   187     //       This functionality is implemented in a package-private method
       
   188     //       to insure that it cannot be overridden by client subclasses.
       
   189     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
       
   190     void dispatchComposedText(String chgText,
       
   191                                            int chgStyles[],
       
   192                                            int chgOffset,
       
   193                                            int chgLength,
       
   194                                            int caretPosition,
       
   195                                            long when) {
       
   196         if (disposed) {
       
   197             return;
       
   198         }
       
   199 
       
   200         // Workaround for deadlock bug on solaris2.6_zh bug#4170760
       
   201         if (chgText == null
       
   202             && chgStyles == null
       
   203             && chgOffset == 0
       
   204             && chgLength == 0
       
   205             && caretPosition == 0
       
   206             && composedText == null
       
   207             && committedText == null)
       
   208             return;
       
   209 
       
   210         // Recalculate chgOffset and chgLength for supplementary char
       
   211         if (composedText != null) {
       
   212             int tmpChgOffset=chgOffset;
       
   213             int tmpChgLength=chgLength;
       
   214             int index = 0;
       
   215             for (int i=0;i < tmpChgOffset; i++,index++){
       
   216                 if (index < composedText.length()
       
   217                     && Character.charCount(composedText.codePointAt(index))==2){
       
   218                     index++;
       
   219                     chgOffset++;
       
   220                 }
       
   221             }
       
   222             // The index keeps value
       
   223             for (int i=0;i < tmpChgLength; i++,index++){
       
   224                 if (index < composedText.length()
       
   225                     && Character.charCount(composedText.codePointAt(index))==2){
       
   226                     index++;
       
   227                     chgLength++;
       
   228                 }
       
   229             }
       
   230         }
       
   231 
       
   232         // Replace control character with a square box
       
   233         if (chgText != null) {
       
   234             StringBuffer newChgText = new StringBuffer();
       
   235             for (int i=0; i < chgText.length(); i++){
       
   236                 char c = chgText.charAt(i);
       
   237                 if (Character.isISOControl(c)){
       
   238                     c = '\u25A1';
       
   239                 }
       
   240                 newChgText.append(c);
       
   241             }
       
   242             chgText = new String(newChgText);
       
   243         }
       
   244 
       
   245         if (composedText == null) {
       
   246             // TODO: avoid reallocation of those buffers
       
   247             composedText = new StringBuffer(INITIAL_SIZE);
       
   248             rawFeedbacks = new IntBuffer(INITIAL_SIZE);
       
   249         }
       
   250         if (chgLength > 0) {
       
   251             if (chgText == null && chgStyles != null) {
       
   252                 rawFeedbacks.replace(chgOffset, chgStyles);
       
   253             } else {
       
   254                 if (chgLength == composedText.length()) {
       
   255                     // optimization for the special case to replace the
       
   256                     // entire previous text
       
   257                     composedText = new StringBuffer(INITIAL_SIZE);
       
   258                     rawFeedbacks = new IntBuffer(INITIAL_SIZE);
       
   259                 } else {
       
   260                     if (composedText.length() > 0) {
       
   261                         if (chgOffset+chgLength < composedText.length()) {
       
   262                             String text;
       
   263                             text = composedText.toString().substring(chgOffset+chgLength,
       
   264                                                                      composedText.length());
       
   265                             composedText.setLength(chgOffset);
       
   266                             composedText.append(text);
       
   267                         } else {
       
   268                             // in case to remove substring from chgOffset
       
   269                             // to the end
       
   270                             composedText.setLength(chgOffset);
       
   271                         }
       
   272                         rawFeedbacks.remove(chgOffset, chgLength);
       
   273                     }
       
   274                 }
       
   275             }
       
   276         }
       
   277         if (chgText != null) {
       
   278             composedText.insert(chgOffset, chgText);
       
   279             if (chgStyles != null) {
       
   280                 // Recalculate chgStyles for supplementary char
       
   281                 if (chgText.length() > chgStyles.length){
       
   282                     int index=0;
       
   283                     int[] newStyles = new int[chgText.length()];
       
   284                     for (int i=0; i < chgStyles.length; i++, index++){
       
   285                         newStyles[index]=chgStyles[i];
       
   286                         if (index < chgText.length()
       
   287                             && Character.charCount(chgText.codePointAt(index))==2){
       
   288                             newStyles[++index]=chgStyles[i];
       
   289                         }
       
   290                     }
       
   291                     chgStyles=newStyles;
       
   292                 }
       
   293                 rawFeedbacks.insert(chgOffset, chgStyles);
       
   294             }
       
   295 
       
   296         }
       
   297 
       
   298         else if (chgStyles != null) {
       
   299             // Recalculate chgStyles to support supplementary char
       
   300             int count=0;
       
   301             for (int i=0; i < chgStyles.length; i++){
       
   302                 if (composedText.length() > chgOffset+i+count
       
   303                     && Character.charCount(composedText.codePointAt(chgOffset+i+count))==2){
       
   304                     count++;
       
   305                 }
       
   306             }
       
   307             if (count>0){
       
   308                 int index=0;
       
   309                 int[] newStyles = new int[chgStyles.length+count];
       
   310                 for (int i=0; i < chgStyles.length; i++, index++){
       
   311                     newStyles[index]=chgStyles[i];
       
   312                     if (composedText.length() > chgOffset+index
       
   313                         && Character.charCount(composedText.codePointAt(chgOffset+index))==2){
       
   314                         newStyles[++index]=chgStyles[i];
       
   315                     }
       
   316                 }
       
   317                 chgStyles=newStyles;
       
   318             }
       
   319             rawFeedbacks.replace(chgOffset, chgStyles);
       
   320         }
       
   321 
       
   322         if (composedText.length() == 0) {
       
   323             composedText = null;
       
   324             rawFeedbacks = null;
       
   325 
       
   326             // if there is any outstanding committed text stored by
       
   327             // dispatchCommittedText(), it has to be sent to the
       
   328             // client component.
       
   329             if (committedText != null) {
       
   330                 dispatchCommittedText(committedText, when);
       
   331                 committedText = null;
       
   332                 return;
       
   333             }
       
   334 
       
   335             // otherwise, send null text to delete client's composed
       
   336             // text.
       
   337             postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
       
   338                                  null,
       
   339                                  0,
       
   340                                  null,
       
   341                                  null,
       
   342                                  when);
       
   343 
       
   344             return;
       
   345         }
       
   346 
       
   347         // Adjust caretPosition for supplementary char
       
   348         for (int i=0; i< caretPosition; i++){
       
   349             if (i < composedText.length()
       
   350                 && Character.charCount(composedText.codePointAt(i))==2){
       
   351                 caretPosition++;
       
   352                 i++;
       
   353             }
       
   354         }
       
   355 
       
   356         // Now sending the composed text to the client
       
   357         int composedOffset;
       
   358         AttributedString inputText;
       
   359 
       
   360         // if there is any partially committed text, concatenate it to
       
   361         // the composed text.
       
   362         if (committedText != null) {
       
   363             composedOffset = committedText.length();
       
   364             inputText = new AttributedString(committedText + composedText);
       
   365             committedText = null;
       
   366         } else {
       
   367             composedOffset = 0;
       
   368             inputText = new AttributedString(composedText.toString());
       
   369         }
       
   370 
       
   371         int currentFeedback;
       
   372         int nextFeedback;
       
   373         int startOffset = 0;
       
   374         int currentOffset;
       
   375         int visiblePosition = 0;
       
   376         TextHitInfo visiblePositionInfo = null;
       
   377 
       
   378         rawFeedbacks.rewind();
       
   379         currentFeedback = rawFeedbacks.getNext();
       
   380         rawFeedbacks.unget();
       
   381         while ((nextFeedback = rawFeedbacks.getNext()) != -1) {
       
   382             if (visiblePosition == 0) {
       
   383                 visiblePosition = nextFeedback & XIMVisibleMask;
       
   384                 if (visiblePosition != 0) {
       
   385                     int index = rawFeedbacks.getOffset() - 1;
       
   386 
       
   387                     if (visiblePosition == XIMVisibleToBackward)
       
   388                         visiblePositionInfo = TextHitInfo.leading(index);
       
   389                     else
       
   390                         visiblePositionInfo = TextHitInfo.trailing(index);
       
   391                 }
       
   392             }
       
   393             nextFeedback &= ~XIMVisibleMask;
       
   394             if (currentFeedback != nextFeedback) {
       
   395                 rawFeedbacks.unget();
       
   396                 currentOffset = rawFeedbacks.getOffset();
       
   397                 inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
       
   398                                        convertVisualFeedbackToHighlight(currentFeedback),
       
   399                                        composedOffset + startOffset,
       
   400                                        composedOffset + currentOffset);
       
   401                 startOffset = currentOffset;
       
   402                 currentFeedback = nextFeedback;
       
   403             }
       
   404         }
       
   405         currentOffset = rawFeedbacks.getOffset();
       
   406         if (currentOffset >= 0) {
       
   407             inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
       
   408                                    convertVisualFeedbackToHighlight(currentFeedback),
       
   409                                    composedOffset + startOffset,
       
   410                                    composedOffset + currentOffset);
       
   411         }
       
   412 
       
   413         postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
       
   414                              inputText.getIterator(),
       
   415                              composedOffset,
       
   416                              TextHitInfo.leading(caretPosition),
       
   417                              visiblePositionInfo,
       
   418                              when);
       
   419     }
       
   420 
       
   421     /* Some IMs need forced Text clear */
       
   422     void clearComposedText(long when) {
       
   423         composedText = null;
       
   424         postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
       
   425                              null, 0, null, null,
       
   426                              when);
       
   427         if (committedText != null && committedText.length() > 0) {
       
   428             dispatchCommittedText(committedText, when);
       
   429         }
       
   430         committedText = null;
       
   431         rawFeedbacks = null;
       
   432     }
       
   433 
       
   434     void clearComposedText() {
       
   435         if (EventQueue.isDispatchThread()) {
       
   436             clearComposedText(EventQueue.getMostRecentEventTime());
       
   437         }
       
   438     }
       
   439 
       
   440     /*
       
   441      * Subclasses should override disposeImpl() instead of dispose(). Client
       
   442      * code should always invoke dispose(), never disposeImpl().
       
   443      */
       
   444     protected synchronized void disposeImpl() {
       
   445         disposeXIC();
       
   446         awtLock();
       
   447         try {
       
   448             clearComposedText();
       
   449         } finally {
       
   450             // Put awtUnlock into finally block in case an exception is thrown in clearComposedText.
       
   451             awtUnlock();
       
   452         }
       
   453         awtFocussedComponent = null;
       
   454         lastXICFocussedComponent = null;
       
   455         needResetXIC = false;
       
   456         savedCompositionState = false;
       
   457         compositionEnableSupported = true;
       
   458     }
       
   459 
       
   460     /**
       
   461      * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
       
   462      */
       
   463     public void setCompositionEnabled(boolean enable) {
       
   464         /* If the composition state is successfully changed, set
       
   465            the savedCompositionState to 'enable'. Otherwise, simply
       
   466            return.
       
   467            setCompositionEnabledNative may throw UnsupportedOperationException.
       
   468            Don't try to catch it since the method may be called by clients.
       
   469            Use package private mthod 'resetCompositionState' if you want the
       
   470            exception to be caught.
       
   471         */
       
   472         boolean pre, post;
       
   473         pre=getCompositionState();
       
   474 
       
   475         if (setCompositionEnabledNative(enable)) {
       
   476             savedCompositionState = enable;
       
   477         }
       
   478 
       
   479         post=getCompositionState();
       
   480         if (pre != post && post == enable){
       
   481             if (enable == false) flushText();
       
   482             if (awtFocussedComponent != null && isActive){
       
   483                 setXICFocus(getPeer(awtFocussedComponent),
       
   484                             true, haveActiveClient());
       
   485             }
       
   486         }
       
   487     }
       
   488 
       
   489     private native void setStatusAreaVisible(boolean value, long data);
       
   490 }