src/java.desktop/aix/classes/sun/awt/X11InputMethod.java
changeset 50350 668463f93ec0
parent 47216 71c04702a3d5
child 52248 2e330da7cbf4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/aix/classes/sun/awt/X11InputMethod.java	Wed May 30 08:19:59 2018 +0200
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.awt;
+
+import java.awt.AWTException;
+import java.awt.EventQueue;
+import java.awt.event.InputMethodEvent;
+import java.awt.font.TextAttribute;
+import java.awt.font.TextHitInfo;
+import java.awt.peer.ComponentPeer;
+import java.text.AttributedString;
+
+import sun.util.logging.PlatformLogger;
+
+/**
+ * Input Method Adapter for XIM for AIX
+ *
+ * @author JavaSoft International
+ */
+public abstract class X11InputMethod extends X11InputMethodBase {
+
+    // to keep the instance of activating if IM resumed
+    static protected X11InputMethod activatedInstance = null;
+
+    /**
+     * Constructs an X11InputMethod instance. It initializes the XIM
+     * environment if it's not done yet.
+     *
+     * @exception AWTException if XOpenIM() failed.
+     */
+    public X11InputMethod() throws AWTException {
+        super();
+    }
+
+    /**
+     * Reset the composition state to the current composition state.
+     */
+    protected void resetCompositionState() {
+        if (compositionEnableSupported && haveActiveClient()) {
+            try {
+                /* Restore the composition mode to the last saved composition
+                   mode. */
+                setCompositionEnabled(savedCompositionState);
+            } catch (UnsupportedOperationException e) {
+                compositionEnableSupported = false;
+            }
+        }
+    }
+
+    /**
+     * Activate input method.
+     */
+    public synchronized void activate() {
+        activatedInstance = this;
+        clientComponentWindow = getClientComponentWindow();
+        if (clientComponentWindow == null)
+            return;
+
+        if (lastXICFocussedComponent != null) {
+            if (log.isLoggable(PlatformLogger.Level.FINE)) {
+                log.fine("XICFocused {0}, AWTFocused {1}",
+                         lastXICFocussedComponent, awtFocussedComponent);
+            }
+            if (lastXICFocussedComponent != awtFocussedComponent) {
+                ComponentPeer lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent);
+                if (lastXICFocussedComponentPeer != null) {
+                    setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive);
+                }
+            }
+            lastXICFocussedComponent = null;
+        }
+
+        if (pData == 0) {
+            if (!createXIC()) {
+                return;
+            }
+            disposed = false;
+        }
+
+        /*  reset input context if necessary and set the XIC focus
+        */
+        resetXICifneeded();
+        ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);
+        setStatusAreaVisible(true, pData);
+
+        if (awtFocussedComponentPeer != null) {
+            setXICFocus(awtFocussedComponentPeer, true, haveActiveClient());
+        }
+        lastXICFocussedComponent = awtFocussedComponent;
+        isLastXICActive = haveActiveClient();
+        isActive = true;
+        if (savedCompositionState) {
+            resetCompositionState();
+        }
+    }
+
+    /**
+     * Deactivate input method.
+     */
+    public synchronized void deactivate(boolean isTemporary) {
+        boolean   isAc =  haveActiveClient();
+        /* Usually as the client component, let's call it component A,
+           loses the focus, this method is called. Then when another client
+           component, let's call it component B,  gets the focus, activate is first called on
+           the previous focused compoent which is A, then endComposition is called on A,
+           deactivate is called on A again. And finally activate is called on the newly
+           focused component B. Here is the call sequence.
+
+           A loses focus               B gains focus
+           -------------> deactivate A -------------> activate A -> endComposition A ->
+           deactivate A -> activate B ----....
+
+           So in order to carry the composition mode across the components sharing the same
+           input context, we save it when deactivate is called so that when activate is
+           called, it can be restored correctly till activate is called on the newly focused
+           component. (See also sun/awt/im/InputContext and bug 6184471).
+           Last note, getCompositionState should be called before setXICFocus since
+           setXICFocus here sets the XIC to 0.
+        */
+        activatedInstance = null;
+        savedCompositionState = getCompositionState();
+
+        if (isTemporary) {
+            //turn the status window off...
+            turnoffStatusWindow();
+            /* Delay resetting the XIC focus until activate is called and the newly
+             * Focused component has a different peer as the last focused component.
+             */
+            lastXICFocussedComponent = awtFocussedComponent;
+        } else {
+            if (awtFocussedComponent != null ) {
+                ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);
+                if (awtFocussedComponentPeer != null) {
+                    setXICFocus(awtFocussedComponentPeer, false, isAc);
+                }
+            }
+            lastXICFocussedComponent = null;
+        }
+
+        isLastXICActive = isAc;
+        isLastTemporary = isTemporary;
+        isActive = false;
+        setStatusAreaVisible(false, pData);
+    }
+
+    // implements java.awt.im.spi.InputMethod.hideWindows
+    public void hideWindows() {
+        if (pData != 0) {
+            setStatusAreaVisible(false, pData);
+            turnoffStatusWindow();
+        }
+    }
+
+    /**
+     * Updates composed text with XIM preedit information and
+     * posts composed text to the awt event queue. The args of
+     * this method correspond to the XIM preedit callback
+     * information. The XIM highlight attributes are translated via
+     * fixed mapping (i.e., independent from any underlying input
+     * method engine). This method is invoked in the AWT Toolkit
+     * (X event loop) thread context and thus inside the AWT Lock.
+     */
+    // NOTE: This method may be called by privileged threads.
+    //       This functionality is implemented in a package-private method
+    //       to insure that it cannot be overridden by client subclasses.
+    //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
+    void dispatchComposedText(String chgText,
+                                           int chgStyles[],
+                                           int chgOffset,
+                                           int chgLength,
+                                           int caretPosition,
+                                           long when) {
+        if (disposed) {
+            return;
+        }
+
+        // Workaround for deadlock bug on solaris2.6_zh bug#4170760
+        if (chgText == null
+            && chgStyles == null
+            && chgOffset == 0
+            && chgLength == 0
+            && caretPosition == 0
+            && composedText == null
+            && committedText == null)
+            return;
+
+        // Recalculate chgOffset and chgLength for supplementary char
+        if (composedText != null) {
+            int tmpChgOffset=chgOffset;
+            int tmpChgLength=chgLength;
+            int index = 0;
+            for (int i=0;i < tmpChgOffset; i++,index++){
+                if (index < composedText.length()
+                    && Character.charCount(composedText.codePointAt(index))==2){
+                    index++;
+                    chgOffset++;
+                }
+            }
+            // The index keeps value
+            for (int i=0;i < tmpChgLength; i++,index++){
+                if (index < composedText.length()
+                    && Character.charCount(composedText.codePointAt(index))==2){
+                    index++;
+                    chgLength++;
+                }
+            }
+        }
+
+        // Replace control character with a square box
+        if (chgText != null) {
+            StringBuffer newChgText = new StringBuffer();
+            for (int i=0; i < chgText.length(); i++){
+                char c = chgText.charAt(i);
+                if (Character.isISOControl(c)){
+                    c = '\u25A1';
+                }
+                newChgText.append(c);
+            }
+            chgText = new String(newChgText);
+        }
+
+        if (composedText == null) {
+            // TODO: avoid reallocation of those buffers
+            composedText = new StringBuffer(INITIAL_SIZE);
+            rawFeedbacks = new IntBuffer(INITIAL_SIZE);
+        }
+        if (chgLength > 0) {
+            if (chgText == null && chgStyles != null) {
+                rawFeedbacks.replace(chgOffset, chgStyles);
+            } else {
+                if (chgLength == composedText.length()) {
+                    // optimization for the special case to replace the
+                    // entire previous text
+                    composedText = new StringBuffer(INITIAL_SIZE);
+                    rawFeedbacks = new IntBuffer(INITIAL_SIZE);
+                } else {
+                    if (composedText.length() > 0) {
+                        if (chgOffset+chgLength < composedText.length()) {
+                            String text;
+                            text = composedText.toString().substring(chgOffset+chgLength,
+                                                                     composedText.length());
+                            composedText.setLength(chgOffset);
+                            composedText.append(text);
+                        } else {
+                            // in case to remove substring from chgOffset
+                            // to the end
+                            composedText.setLength(chgOffset);
+                        }
+                        rawFeedbacks.remove(chgOffset, chgLength);
+                    }
+                }
+            }
+        }
+        if (chgText != null) {
+            composedText.insert(chgOffset, chgText);
+            if (chgStyles != null) {
+                // Recalculate chgStyles for supplementary char
+                if (chgText.length() > chgStyles.length){
+                    int index=0;
+                    int[] newStyles = new int[chgText.length()];
+                    for (int i=0; i < chgStyles.length; i++, index++){
+                        newStyles[index]=chgStyles[i];
+                        if (index < chgText.length()
+                            && Character.charCount(chgText.codePointAt(index))==2){
+                            newStyles[++index]=chgStyles[i];
+                        }
+                    }
+                    chgStyles=newStyles;
+                }
+                rawFeedbacks.insert(chgOffset, chgStyles);
+            }
+
+        }
+
+        else if (chgStyles != null) {
+            // Recalculate chgStyles to support supplementary char
+            int count=0;
+            for (int i=0; i < chgStyles.length; i++){
+                if (composedText.length() > chgOffset+i+count
+                    && Character.charCount(composedText.codePointAt(chgOffset+i+count))==2){
+                    count++;
+                }
+            }
+            if (count>0){
+                int index=0;
+                int[] newStyles = new int[chgStyles.length+count];
+                for (int i=0; i < chgStyles.length; i++, index++){
+                    newStyles[index]=chgStyles[i];
+                    if (composedText.length() > chgOffset+index
+                        && Character.charCount(composedText.codePointAt(chgOffset+index))==2){
+                        newStyles[++index]=chgStyles[i];
+                    }
+                }
+                chgStyles=newStyles;
+            }
+            rawFeedbacks.replace(chgOffset, chgStyles);
+        }
+
+        if (composedText.length() == 0) {
+            composedText = null;
+            rawFeedbacks = null;
+
+            // if there is any outstanding committed text stored by
+            // dispatchCommittedText(), it has to be sent to the
+            // client component.
+            if (committedText != null) {
+                dispatchCommittedText(committedText, when);
+                committedText = null;
+                return;
+            }
+
+            // otherwise, send null text to delete client's composed
+            // text.
+            postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
+                                 null,
+                                 0,
+                                 null,
+                                 null,
+                                 when);
+
+            return;
+        }
+
+        // Adjust caretPosition for supplementary char
+        for (int i=0; i< caretPosition; i++){
+            if (i < composedText.length()
+                && Character.charCount(composedText.codePointAt(i))==2){
+                caretPosition++;
+                i++;
+            }
+        }
+
+        // Now sending the composed text to the client
+        int composedOffset;
+        AttributedString inputText;
+
+        // if there is any partially committed text, concatenate it to
+        // the composed text.
+        if (committedText != null) {
+            composedOffset = committedText.length();
+            inputText = new AttributedString(committedText + composedText);
+            committedText = null;
+        } else {
+            composedOffset = 0;
+            inputText = new AttributedString(composedText.toString());
+        }
+
+        int currentFeedback;
+        int nextFeedback;
+        int startOffset = 0;
+        int currentOffset;
+        int visiblePosition = 0;
+        TextHitInfo visiblePositionInfo = null;
+
+        rawFeedbacks.rewind();
+        currentFeedback = rawFeedbacks.getNext();
+        rawFeedbacks.unget();
+        while ((nextFeedback = rawFeedbacks.getNext()) != -1) {
+            if (visiblePosition == 0) {
+                visiblePosition = nextFeedback & XIMVisibleMask;
+                if (visiblePosition != 0) {
+                    int index = rawFeedbacks.getOffset() - 1;
+
+                    if (visiblePosition == XIMVisibleToBackward)
+                        visiblePositionInfo = TextHitInfo.leading(index);
+                    else
+                        visiblePositionInfo = TextHitInfo.trailing(index);
+                }
+            }
+            nextFeedback &= ~XIMVisibleMask;
+            if (currentFeedback != nextFeedback) {
+                rawFeedbacks.unget();
+                currentOffset = rawFeedbacks.getOffset();
+                inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
+                                       convertVisualFeedbackToHighlight(currentFeedback),
+                                       composedOffset + startOffset,
+                                       composedOffset + currentOffset);
+                startOffset = currentOffset;
+                currentFeedback = nextFeedback;
+            }
+        }
+        currentOffset = rawFeedbacks.getOffset();
+        if (currentOffset >= 0) {
+            inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
+                                   convertVisualFeedbackToHighlight(currentFeedback),
+                                   composedOffset + startOffset,
+                                   composedOffset + currentOffset);
+        }
+
+        postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
+                             inputText.getIterator(),
+                             composedOffset,
+                             TextHitInfo.leading(caretPosition),
+                             visiblePositionInfo,
+                             when);
+    }
+
+    /* Some IMs need forced Text clear */
+    void clearComposedText(long when) {
+        composedText = null;
+        postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
+                             null, 0, null, null,
+                             when);
+        if (committedText != null && committedText.length() > 0) {
+            dispatchCommittedText(committedText, when);
+        }
+        committedText = null;
+        rawFeedbacks = null;
+    }
+
+    void clearComposedText() {
+        if (EventQueue.isDispatchThread()) {
+            clearComposedText(EventQueue.getMostRecentEventTime());
+        }
+    }
+
+    /*
+     * Subclasses should override disposeImpl() instead of dispose(). Client
+     * code should always invoke dispose(), never disposeImpl().
+     */
+    protected synchronized void disposeImpl() {
+        disposeXIC();
+        awtLock();
+        try {
+            clearComposedText();
+        } finally {
+            // Put awtUnlock into finally block in case an exception is thrown in clearComposedText.
+            awtUnlock();
+        }
+        awtFocussedComponent = null;
+        lastXICFocussedComponent = null;
+        needResetXIC = false;
+        savedCompositionState = false;
+        compositionEnableSupported = true;
+    }
+
+    /**
+     * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
+     */
+    public void setCompositionEnabled(boolean enable) {
+        /* If the composition state is successfully changed, set
+           the savedCompositionState to 'enable'. Otherwise, simply
+           return.
+           setCompositionEnabledNative may throw UnsupportedOperationException.
+           Don't try to catch it since the method may be called by clients.
+           Use package private mthod 'resetCompositionState' if you want the
+           exception to be caught.
+        */
+        boolean pre, post;
+        pre=getCompositionState();
+
+        if (setCompositionEnabledNative(enable)) {
+            savedCompositionState = enable;
+        }
+
+        post=getCompositionState();
+        if (pre != post && post == enable){
+            if (enable == false) flushText();
+            if (awtFocussedComponent != null && isActive){
+                setXICFocus(getPeer(awtFocussedComponent),
+                            true, haveActiveClient());
+            }
+        }
+    }
+
+    private native void setStatusAreaVisible(boolean value, long data);
+}