/* * Copyright (c) 2011, 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 com.sun.javafx.webkit; import com.sun.javafx.scene.SceneHelper; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import com.sun.javafx.logging.PlatformLogger; import com.sun.javafx.scene.input.ExtendedInputMethodRequests; import com.sun.webkit.Invoker; import javafx.geometry.Point2D; import javafx.scene.input.InputMethodEvent; import javafx.scene.input.InputMethodHighlight; import javafx.scene.input.InputMethodTextRun; import javafx.scene.web.WebView; import com.sun.webkit.InputMethodClient; import com.sun.webkit.WebPage; import com.sun.webkit.event.WCInputMethodEvent; import com.sun.webkit.graphics.WCPoint; public final class InputMethodClientImpl implements InputMethodClient, ExtendedInputMethodRequests { private static final PlatformLogger log = PlatformLogger.getLogger(InputMethodClientImpl.class.getName()); private final WeakReference wvRef; private final WebPage webPage; // the state of the last setInputMethodState() call. private boolean state; public InputMethodClientImpl(WebView wv, WebPage webPage) { this.wvRef = new WeakReference(wv); this.webPage = webPage; if (webPage != null) { webPage.setInputMethodClient(this); } } public void activateInputMethods(final boolean doActivate) { WebView wv = wvRef.get(); if (wv != null && wv.getScene() != null) { SceneHelper.enableInputMethodEvents(wv.getScene(), doActivate); } state = doActivate; } public boolean getInputMethodState() { return state; } /** * Converts the given InputMethodEvent to a WCInputMethodEvent. */ public static WCInputMethodEvent convertToWCInputMethodEvent(InputMethodEvent ie) { List underlines = new ArrayList(); StringBuilder composed = new StringBuilder(); int pos = 0; // Scan the given composedText to find input method highlight attribute runs. for (InputMethodTextRun run : ie.getComposed()) { String rawText = run.getText(); // Convert highlight information of the attribute run into a // CompositionUnderline. InputMethodHighlight imh = run.getHighlight(); underlines.add(pos); underlines.add(pos + rawText.length()); // WebKit CompostionUnderline supports only two kinds of highlighting // attributes, thin and thick underlines. The SELECTED_CONVERTED // and SELECTED_RAW attributes of JavaFX are mapped to the thick one. underlines.add((imh == InputMethodHighlight.SELECTED_CONVERTED || imh == InputMethodHighlight.SELECTED_RAW) ? 1 : 0); pos += rawText.length(); composed.append(rawText); } int size = underlines.size(); // In case there's no highlight information, create an underline element // for the entire text if (size == 0) { underlines.add(0); underlines.add(pos); underlines.add(0); // thin underline size = underlines.size(); } int[] attributes = new int[size]; for (int i = 0; i < size; i++) { attributes[i] = underlines.get(i); } return new WCInputMethodEvent(ie.getCommitted(), composed.toString(), attributes, ie.getCaretPosition()); } // InputMethodRequests implementation public Point2D getTextLocation(int offset) { FutureTask f = new FutureTask<>(() -> { int[] loc = webPage.getClientTextLocation(offset); WCPoint point = webPage.getPageClient().windowToScreen( // We need lower left corner of the char bounds rectangle here new WCPoint(loc[0], loc[1] + loc[3])); return new Point2D(point.getIntX(), point.getIntY()); }); Invoker.getInvoker().invokeOnEventThread(f); Point2D result = null; try { result = f.get(); } catch (ExecutionException ex) { log.severe("InputMethodClientImpl.getTextLocation " + ex); } catch (InterruptedException ex) { log.severe("InputMethodClientImpl.getTextLocation InterruptedException" + ex); } return result; } public int getLocationOffset(int x, int y) { FutureTask f = new FutureTask<>(() -> { WCPoint point = webPage.getPageClient().windowToScreen(new WCPoint(0, 0)); return webPage.getClientLocationOffset(x - point.getIntX(), y - point.getIntY()); }); Invoker.getInvoker().invokeOnEventThread(f); int location = 0; try { location = f.get(); } catch (ExecutionException ex) { log.severe("InputMethodClientImpl.getLocationOffset " + ex); } catch (InterruptedException ex) { log.severe("InputMethodClientImpl.getTextLocation InterruptedException" + ex); } return location; } public void cancelLatestCommittedText() { // "Undo commit" is not supported. } public String getSelectedText() { return webPage.getClientSelectedText(); } @Override public int getInsertPositionOffset() { return webPage.getClientInsertPositionOffset(); } @Override public String getCommittedText(int begin, int end) { try { return webPage.getClientCommittedText().substring(begin, end); } catch (StringIndexOutOfBoundsException e) { throw new IllegalArgumentException(e); } } @Override public int getCommittedTextLength() { return webPage.getClientCommittedTextLength(); } }