Path: blob/master/src/java.desktop/aix/classes/sun/awt/X11InputMethod.java
41153 views
/*1* Copyright (c) 1997, 2021, 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.EventQueue;29import java.awt.event.InputMethodEvent;30import java.awt.font.TextAttribute;31import java.awt.font.TextHitInfo;32import java.awt.peer.ComponentPeer;33import java.text.AttributedString;3435import sun.util.logging.PlatformLogger;3637/**38* Input Method Adapter for XIM for AIX39*40* @author JavaSoft International41*/42public abstract class X11InputMethod extends X11InputMethodBase {4344// to keep the instance of activating if IM resumed45protected static X11InputMethod activatedInstance = null;4647/**48* Constructs an X11InputMethod instance. It initializes the XIM49* environment if it's not done yet.50*51* @exception AWTException if XOpenIM() failed.52*/53public X11InputMethod() throws AWTException {54super();55}5657/**58* Reset the composition state to the current composition state.59*/60protected void resetCompositionState() {61if (compositionEnableSupported && haveActiveClient()) {62try {63/* Restore the composition mode to the last saved composition64mode. */65setCompositionEnabled(savedCompositionState);66} catch (UnsupportedOperationException e) {67compositionEnableSupported = false;68}69}70}7172/**73* Activate input method.74*/75public synchronized void activate() {76activatedInstance = this;77clientComponentWindow = getClientComponentWindow();78if (clientComponentWindow == null)79return;8081if (lastXICFocussedComponent != null) {82if (log.isLoggable(PlatformLogger.Level.FINE)) {83log.fine("XICFocused {0}, AWTFocused {1}",84lastXICFocussedComponent, awtFocussedComponent);85}86if (lastXICFocussedComponent != awtFocussedComponent) {87ComponentPeer lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent);88if (lastXICFocussedComponentPeer != null) {89setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive);90}91}92lastXICFocussedComponent = null;93}9495if (pData == 0) {96if (!createXIC()) {97return;98}99disposed = false;100}101102/* reset input context if necessary and set the XIC focus103*/104resetXICifneeded();105ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);106setStatusAreaVisible(true, pData);107108if (awtFocussedComponentPeer != null) {109setXICFocus(awtFocussedComponentPeer, true, haveActiveClient());110}111lastXICFocussedComponent = awtFocussedComponent;112isLastXICActive = haveActiveClient();113isActive = true;114if (savedCompositionState) {115resetCompositionState();116}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*/142activatedInstance = null;143savedCompositionState = getCompositionState();144145if (isTemporary) {146//turn the status window off...147turnoffStatusWindow();148/* Delay resetting the XIC focus until activate is called and the newly149* Focused component has a different peer as the last focused component.150*/151lastXICFocussedComponent = awtFocussedComponent;152} else {153if (awtFocussedComponent != null ) {154ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);155if (awtFocussedComponentPeer != null) {156setXICFocus(awtFocussedComponentPeer, false, isAc);157}158}159lastXICFocussedComponent = null;160}161162isLastXICActive = isAc;163isLastTemporary = isTemporary;164isActive = false;165setStatusAreaVisible(false, pData);166}167168// implements java.awt.im.spi.InputMethod.hideWindows169public void hideWindows() {170if (pData != 0) {171setStatusAreaVisible(false, pData);172turnoffStatusWindow();173}174}175176/**177* Updates composed text with XIM preedit information and178* posts composed text to the awt event queue. The args of179* this method correspond to the XIM preedit callback180* information. The XIM highlight attributes are translated via181* fixed mapping (i.e., independent from any underlying input182* method engine). This method is invoked in the AWT Toolkit183* (X event loop) thread context and thus inside the AWT Lock.184*/185// NOTE: This method may be called by privileged threads.186// This functionality is implemented in a package-private method187// to insure that it cannot be overridden by client subclasses.188// DO NOT INVOKE CLIENT CODE ON THIS THREAD!189void dispatchComposedText(String chgText,190int[] chgStyles,191int chgOffset,192int chgLength,193int caretPosition,194long when) {195if (disposed) {196return;197}198199// Workaround for deadlock bug on solaris2.6_zh bug#4170760200if (chgText == null201&& chgStyles == null202&& chgOffset == 0203&& chgLength == 0204&& caretPosition == 0205&& composedText == null206&& committedText == null)207return;208209// Recalculate chgOffset and chgLength for supplementary char210if (composedText != null) {211int tmpChgOffset=chgOffset;212int tmpChgLength=chgLength;213int index = 0;214for (int i=0;i < tmpChgOffset; i++,index++){215if (index < composedText.length()216&& Character.charCount(composedText.codePointAt(index))==2){217index++;218chgOffset++;219}220}221// The index keeps value222for (int i=0;i < tmpChgLength; i++,index++){223if (index < composedText.length()224&& Character.charCount(composedText.codePointAt(index))==2){225index++;226chgLength++;227}228}229}230231// Replace control character with a square box232if (chgText != null) {233StringBuilder newChgText = new StringBuilder();234for (int i=0; i < chgText.length(); i++){235char c = chgText.charAt(i);236if (Character.isISOControl(c)){237c = '\u25A1';238}239newChgText.append(c);240}241chgText = new String(newChgText);242}243244if (composedText == null) {245// TODO: avoid reallocation of those buffers246composedText = new StringBuffer(INITIAL_SIZE);247rawFeedbacks = new IntBuffer(INITIAL_SIZE);248}249if (chgLength > 0) {250if (chgText == null && chgStyles != null) {251rawFeedbacks.replace(chgOffset, chgStyles);252} else {253if (chgLength == composedText.length()) {254// optimization for the special case to replace the255// entire previous text256composedText = new StringBuffer(INITIAL_SIZE);257rawFeedbacks = new IntBuffer(INITIAL_SIZE);258} else {259if (composedText.length() > 0) {260if (chgOffset+chgLength < composedText.length()) {261String text;262text = composedText.toString().substring(chgOffset+chgLength,263composedText.length());264composedText.setLength(chgOffset);265composedText.append(text);266} else {267// in case to remove substring from chgOffset268// to the end269composedText.setLength(chgOffset);270}271rawFeedbacks.remove(chgOffset, chgLength);272}273}274}275}276if (chgText != null) {277composedText.insert(chgOffset, chgText);278if (chgStyles != null) {279// Recalculate chgStyles for supplementary char280if (chgText.length() > chgStyles.length){281int index=0;282int[] newStyles = new int[chgText.length()];283for (int i=0; i < chgStyles.length; i++, index++){284newStyles[index]=chgStyles[i];285if (index < chgText.length()286&& Character.charCount(chgText.codePointAt(index))==2){287newStyles[++index]=chgStyles[i];288}289}290chgStyles=newStyles;291}292rawFeedbacks.insert(chgOffset, chgStyles);293}294295}296297else if (chgStyles != null) {298// Recalculate chgStyles to support supplementary char299int count=0;300for (int i=0; i < chgStyles.length; i++){301if (composedText.length() > chgOffset+i+count302&& Character.charCount(composedText.codePointAt(chgOffset+i+count))==2){303count++;304}305}306if (count>0){307int index=0;308int[] newStyles = new int[chgStyles.length+count];309for (int i=0; i < chgStyles.length; i++, index++){310newStyles[index]=chgStyles[i];311if (composedText.length() > chgOffset+index312&& Character.charCount(composedText.codePointAt(chgOffset+index))==2){313newStyles[++index]=chgStyles[i];314}315}316chgStyles=newStyles;317}318rawFeedbacks.replace(chgOffset, chgStyles);319}320321if (composedText.length() == 0) {322composedText = null;323rawFeedbacks = null;324325// if there is any outstanding committed text stored by326// dispatchCommittedText(), it has to be sent to the327// client component.328if (committedText != null) {329dispatchCommittedText(committedText, when);330committedText = null;331return;332}333334// otherwise, send null text to delete client's composed335// text.336postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,337null,3380,339null,340null,341when);342343return;344}345346// Adjust caretPosition for supplementary char347for (int i=0; i< caretPosition; i++){348if (i < composedText.length()349&& Character.charCount(composedText.codePointAt(i))==2){350caretPosition++;351i++;352}353}354355// Now sending the composed text to the client356int composedOffset;357AttributedString inputText;358359// if there is any partially committed text, concatenate it to360// the composed text.361if (committedText != null) {362composedOffset = committedText.length();363inputText = new AttributedString(committedText + composedText);364committedText = null;365} else {366composedOffset = 0;367inputText = new AttributedString(composedText.toString());368}369370int currentFeedback;371int nextFeedback;372int startOffset = 0;373int currentOffset;374int visiblePosition = 0;375TextHitInfo visiblePositionInfo = null;376377rawFeedbacks.rewind();378currentFeedback = rawFeedbacks.getNext();379rawFeedbacks.unget();380while ((nextFeedback = rawFeedbacks.getNext()) != -1) {381if (visiblePosition == 0) {382visiblePosition = nextFeedback & XIMVisibleMask;383if (visiblePosition != 0) {384int index = rawFeedbacks.getOffset() - 1;385386if (visiblePosition == XIMVisibleToBackward)387visiblePositionInfo = TextHitInfo.leading(index);388else389visiblePositionInfo = TextHitInfo.trailing(index);390}391}392nextFeedback &= ~XIMVisibleMask;393if (currentFeedback != nextFeedback) {394rawFeedbacks.unget();395currentOffset = rawFeedbacks.getOffset();396inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,397convertVisualFeedbackToHighlight(currentFeedback),398composedOffset + startOffset,399composedOffset + currentOffset);400startOffset = currentOffset;401currentFeedback = nextFeedback;402}403}404currentOffset = rawFeedbacks.getOffset();405if (currentOffset >= 0) {406inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,407convertVisualFeedbackToHighlight(currentFeedback),408composedOffset + startOffset,409composedOffset + currentOffset);410}411412postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,413inputText.getIterator(),414composedOffset,415TextHitInfo.leading(caretPosition),416visiblePositionInfo,417when);418}419420/* Some IMs need forced Text clear */421void clearComposedText(long when) {422composedText = null;423postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,424null, 0, null, null,425when);426if (committedText != null && committedText.length() > 0) {427dispatchCommittedText(committedText, when);428}429committedText = null;430rawFeedbacks = null;431}432433void clearComposedText() {434if (EventQueue.isDispatchThread()) {435clearComposedText(EventQueue.getMostRecentEventTime());436}437}438439/*440* Subclasses should override disposeImpl() instead of dispose(). Client441* code should always invoke dispose(), never disposeImpl().442*/443protected synchronized void disposeImpl() {444disposeXIC();445awtLock();446try {447clearComposedText();448} finally {449// Put awtUnlock into finally block in case an exception is thrown in clearComposedText.450awtUnlock();451}452awtFocussedComponent = null;453lastXICFocussedComponent = null;454needResetXIC = false;455savedCompositionState = false;456compositionEnableSupported = true;457}458459/**460* @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)461*/462public void setCompositionEnabled(boolean enable) {463/* If the composition state is successfully changed, set464the savedCompositionState to 'enable'. Otherwise, simply465return.466setCompositionEnabledNative may throw UnsupportedOperationException.467Don't try to catch it since the method may be called by clients.468Use package private mthod 'resetCompositionState' if you want the469exception to be caught.470*/471boolean pre, post;472pre=getCompositionState();473474if (setCompositionEnabledNative(enable)) {475savedCompositionState = enable;476}477478post=getCompositionState();479if (pre != post && post == enable){480if (enable == false) flushText();481if (awtFocussedComponent != null && isActive){482setXICFocus(getPeer(awtFocussedComponent),483true, haveActiveClient());484}485}486}487488private native void setStatusAreaVisible(boolean value, long data);489}490491492