Path: blob/master/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java
41152 views
/*1* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt;2627import java.awt.AWTException;28import java.awt.event.InputMethodEvent;29import java.awt.font.TextAttribute;30import java.awt.font.TextHitInfo;31import java.awt.peer.ComponentPeer;32import java.text.AttributedString;3334import sun.util.logging.PlatformLogger;3536/**37* Input Method Adapter for XIM38*39* @author JavaSoft International40*/41public abstract class X11InputMethod extends X11InputMethodBase {4243/**44* Constructs an X11InputMethod instance. It initializes the XIM45* environment if it's not done yet.46*47* @exception AWTException if XOpenIM() failed.48*/49public X11InputMethod() throws AWTException {50super();51}5253/**54* Reset the composition state to the current composition state.55*/56protected void resetCompositionState() {57if (compositionEnableSupported && haveActiveClient()) {58try {59/* Restore the composition mode to the last saved composition60mode. */61setCompositionEnabled(savedCompositionState);62} catch (UnsupportedOperationException e) {63compositionEnableSupported = false;64}65}66}6768/**69* Activate input method.70*/71public synchronized void activate() {72clientComponentWindow = getClientComponentWindow();73if (clientComponentWindow == null)74return;7576if (lastXICFocussedComponent != null) {77if (log.isLoggable(PlatformLogger.Level.FINE)) {78log.fine("XICFocused {0}, AWTFocused {1}",79lastXICFocussedComponent, awtFocussedComponent);80}81}8283if (pData == 0) {84if (!createXIC()) {85return;86}87disposed = false;88}8990/* reset input context if necessary and set the XIC focus91*/92resetXICifneeded();93ComponentPeer lastXICFocussedComponentPeer = null;94ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);9596if (lastXICFocussedComponent != null) {97lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent);98}99100/* If the last XIC focussed component has a different peer as the101current focussed component, change the XIC focus to the newly102focussed component.103*/104if (isLastTemporary || lastXICFocussedComponentPeer != awtFocussedComponentPeer ||105isLastXICActive != haveActiveClient()) {106if (lastXICFocussedComponentPeer != null) {107setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive);108}109if (awtFocussedComponentPeer != null) {110setXICFocus(awtFocussedComponentPeer, true, haveActiveClient());111}112lastXICFocussedComponent = awtFocussedComponent;113isLastXICActive = haveActiveClient();114}115resetCompositionState();116isActive = true;117}118119/**120* Deactivate input method.121*/122public synchronized void deactivate(boolean isTemporary) {123boolean isAc = haveActiveClient();124/* Usually as the client component, let's call it component A,125loses the focus, this method is called. Then when another client126component, let's call it component B, gets the focus, activate is first called on127the previous focused compoent which is A, then endComposition is called on A,128deactivate is called on A again. And finally activate is called on the newly129focused component B. Here is the call sequence.130131A loses focus B gains focus132-------------> deactivate A -------------> activate A -> endComposition A ->133deactivate A -> activate B ----....134135So in order to carry the composition mode across the components sharing the same136input context, we save it when deactivate is called so that when activate is137called, it can be restored correctly till activate is called on the newly focused138component. (See also sun/awt/im/InputContext and bug 6184471).139Last note, getCompositionState should be called before setXICFocus since140setXICFocus here sets the XIC to 0.141*/142savedCompositionState = getCompositionState();143144if (isTemporary) {145//turn the status window off...146turnoffStatusWindow();147}148149/* Delay resetting the XIC focus until activate is called and the newly150* Focused component has a different peer as the last focused component.151*/152lastXICFocussedComponent = awtFocussedComponent;153isLastXICActive = isAc;154isLastTemporary = isTemporary;155isActive = false;156}157158// implements java.awt.im.spi.InputMethod.hideWindows159public void hideWindows() {160// ??? need real implementation161}162163/**164* Updates composed text with XIM preedit information and165* posts composed text to the awt event queue. The args of166* this method correspond to the XIM preedit callback167* information. The XIM highlight attributes are translated via168* fixed mapping (i.e., independent from any underlying input169* method engine). This method is invoked in the AWT Toolkit170* (X event loop) thread context and thus inside the AWT Lock.171*/172// NOTE: This method may be called by privileged threads.173// This functionality is implemented in a package-private method174// to insure that it cannot be overridden by client subclasses.175// DO NOT INVOKE CLIENT CODE ON THIS THREAD!176void dispatchComposedText(String chgText,177int[] chgStyles,178int chgOffset,179int chgLength,180int caretPosition,181long when) {182if (disposed) {183return;184}185186// Workaround for deadlock bug on solaris2.6_zh bug#4170760187if (chgText == null188&& chgStyles == null189&& chgOffset == 0190&& chgLength == 0191&& caretPosition == 0192&& composedText == null193&& committedText == null)194return;195196if (composedText == null) {197// TODO: avoid reallocation of those buffers198composedText = new StringBuffer(INITIAL_SIZE);199rawFeedbacks = new IntBuffer(INITIAL_SIZE);200}201if (chgLength > 0) {202if (chgText == null && chgStyles != null) {203rawFeedbacks.replace(chgOffset, chgStyles);204} else {205if (chgLength == composedText.length()) {206// optimization for the special case to replace the207// entire previous text208composedText = new StringBuffer(INITIAL_SIZE);209rawFeedbacks = new IntBuffer(INITIAL_SIZE);210} else {211if (composedText.length() > 0) {212if (chgOffset+chgLength < composedText.length()) {213String text;214text = composedText.toString().substring(chgOffset+chgLength,215composedText.length());216composedText.setLength(chgOffset);217composedText.append(text);218} else {219// in case to remove substring from chgOffset220// to the end221composedText.setLength(chgOffset);222}223rawFeedbacks.remove(chgOffset, chgLength);224}225}226}227}228if (chgText != null) {229composedText.insert(chgOffset, chgText);230if (chgStyles != null)231rawFeedbacks.insert(chgOffset, chgStyles);232}233234if (composedText.length() == 0) {235composedText = null;236rawFeedbacks = null;237238// if there is any outstanding committed text stored by239// dispatchCommittedText(), it has to be sent to the240// client component.241if (committedText != null) {242dispatchCommittedText(committedText, when);243committedText = null;244return;245}246247// otherwise, send null text to delete client's composed248// text.249postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,250null,2510,252null,253null,254when);255256return;257}258259// Now sending the composed text to the client260int composedOffset;261AttributedString inputText;262263// if there is any partially committed text, concatenate it to264// the composed text.265if (committedText != null) {266composedOffset = committedText.length();267inputText = new AttributedString(committedText + composedText);268committedText = null;269} else {270composedOffset = 0;271inputText = new AttributedString(composedText.toString());272}273274int currentFeedback;275int nextFeedback;276int startOffset = 0;277int currentOffset;278int visiblePosition = 0;279TextHitInfo visiblePositionInfo = null;280281rawFeedbacks.rewind();282currentFeedback = rawFeedbacks.getNext();283rawFeedbacks.unget();284while ((nextFeedback = rawFeedbacks.getNext()) != -1) {285if (visiblePosition == 0) {286visiblePosition = nextFeedback & XIMVisibleMask;287if (visiblePosition != 0) {288int index = rawFeedbacks.getOffset() - 1;289290if (visiblePosition == XIMVisibleToBackward)291visiblePositionInfo = TextHitInfo.leading(index);292else293visiblePositionInfo = TextHitInfo.trailing(index);294}295}296nextFeedback &= ~XIMVisibleMask;297if (currentFeedback != nextFeedback) {298rawFeedbacks.unget();299currentOffset = rawFeedbacks.getOffset();300inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,301convertVisualFeedbackToHighlight(currentFeedback),302composedOffset + startOffset,303composedOffset + currentOffset);304startOffset = currentOffset;305currentFeedback = nextFeedback;306}307}308currentOffset = rawFeedbacks.getOffset();309if (currentOffset >= 0) {310inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,311convertVisualFeedbackToHighlight(currentFeedback),312composedOffset + startOffset,313composedOffset + currentOffset);314}315316postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,317inputText.getIterator(),318composedOffset,319TextHitInfo.leading(caretPosition),320visiblePositionInfo,321when);322}323324/*325* Subclasses should override disposeImpl() instead of dispose(). Client326* code should always invoke dispose(), never disposeImpl().327*/328protected synchronized void disposeImpl() {329disposeXIC();330awtLock();331composedText = null;332committedText = null;333rawFeedbacks = null;334awtUnlock();335awtFocussedComponent = null;336lastXICFocussedComponent = null;337}338339/**340* @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)341*/342public void setCompositionEnabled(boolean enable) {343/* If the composition state is successfully changed, set344the savedCompositionState to 'enable'. Otherwise, simply345return.346setCompositionEnabledNative may throw UnsupportedOperationException.347Don't try to catch it since the method may be called by clients.348Use package private mthod 'resetCompositionState' if you want the349exception to be caught.350*/351if (setCompositionEnabledNative(enable)) {352savedCompositionState = enable;353}354}355}356357358