Path: blob/master/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java
41640 views
/*1* Copyright (c) 2011, 2016, 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.lwawt.macosx;2627import java.awt.Component;28import java.awt.Point;29import java.awt.Rectangle;30import java.awt.geom.Rectangle2D;31import java.util.concurrent.Callable;3233import javax.accessibility.Accessible;34import javax.accessibility.AccessibleContext;35import javax.accessibility.AccessibleEditableText;36import javax.accessibility.AccessibleText;37import javax.swing.text.Element;38import javax.swing.text.JTextComponent;3940class CAccessibleText {41static AccessibleEditableText getAccessibleEditableText(final Accessible a, final Component c) {42if (a == null) return null;4344return CAccessibility.invokeAndWait(new Callable<AccessibleEditableText>() {45public AccessibleEditableText call() throws Exception {46final AccessibleContext ac = a.getAccessibleContext();47if (ac == null) return null;48return ac.getAccessibleEditableText();49}50}, c);51}5253static String getSelectedText(final Accessible a, final Component c) {54if (a == null) return null;5556return CAccessibility.invokeAndWait(new Callable<String>() {57public String call() throws Exception {58final AccessibleContext ac = a.getAccessibleContext();59if (ac == null) return null;6061final AccessibleText at = ac.getAccessibleText();62if (at == null) return null;6364return at.getSelectedText();65}66}, c);67}6869// replace the currently selected text with newText70static void setSelectedText(final Accessible a, final Component c, final String newText) {71if (a == null) return;7273CAccessibility.invokeLater(new Runnable() {74public void run() {75final AccessibleContext ac = a.getAccessibleContext();76if (ac == null) return;7778final AccessibleEditableText aet = ac.getAccessibleEditableText();79if (aet == null) return;8081final int selectionStart = aet.getSelectionStart();82final int selectionEnd = aet.getSelectionEnd();83aet.replaceText(selectionStart, selectionEnd, newText);84}85}, c);86}8788static void setSelectedTextRange(final Accessible a, final Component c, final int startIndex, final int endIndex) {89if (a == null) return;9091CAccessibility.invokeLater(new Runnable() {92public void run() {93final AccessibleContext ac = a.getAccessibleContext();94if (ac == null) return;9596final AccessibleEditableText aet = ac.getAccessibleEditableText();97if (aet == null) return;9899final boolean validRange = (startIndex >= 0) && (endIndex >= startIndex) && (endIndex <= aet.getCharCount());100if (!validRange) return;101102aet.selectText(startIndex, endIndex);103}104}, c);105}106107static String getTextRange(final AccessibleEditableText aet, final int start, final int stop, final Component c) {108if (aet == null) return null;109110return CAccessibility.invokeAndWait(new Callable<String>() {111public String call() throws Exception {112return aet.getTextRange(start, stop);113}114}, c);115}116117static int getCharacterIndexAtPosition(final Accessible a, final Component c, final int x, final int y) {118if (a == null) return 0;119120return CAccessibility.invokeAndWait(new Callable<Integer>() {121public Integer call() throws Exception {122final AccessibleContext ac = a.getAccessibleContext();123if (ac == null) return null;124final AccessibleText at = ac.getAccessibleText();125if (at == null) return null;126// (x, y) passed in as java screen coords - (0, 0) at upper-left corner of screen.127// Convert to java component-local coords128final Point componentLocation = ac.getAccessibleComponent().getLocationOnScreen();129final int localX = x - (int)componentLocation.getX();130final int localY = y - (int)componentLocation.getY();131132return at.getIndexAtPoint(new Point(localX, localY));133}134}, c);135}136137static int[] getSelectedTextRange(final Accessible a, final Component c) {138if (a == null) return new int[2];139140return CAccessibility.invokeAndWait(new Callable<int[]>() {141public int[] call() {142final AccessibleContext ac = a.getAccessibleContext();143if (ac == null) return new int[2];144145final AccessibleText at = ac.getAccessibleText();146if (at == null) return new int[2];147148final int[] ret = new int[2];149ret[0] = at.getSelectionStart();150ret[1] = at.getSelectionEnd();151return ret;152}153}, c);154}155156157static int[] getVisibleCharacterRange(final Accessible a, final Component c) {158if (a == null) return null;159return CAccessibility.invokeAndWait(new Callable<int[]>() {160public int[] call() {161return getVisibleCharacterRange(a);162}163}, c);164}165166static int getLineNumberForIndex(final Accessible a, final Component c, final int index) {167if (a == null) return 0;168return CAccessibility.invokeAndWait(new Callable<Integer>() {169public Integer call() {170return Integer.valueOf(getLineNumberForIndex(a, index));171}172}, c);173}174175static int getLineNumberForInsertionPoint(final Accessible a, final Component c) {176if (a == null) return 0;177return CAccessibility.invokeAndWait(new Callable<Integer>() {178public Integer call() {179return Integer.valueOf(getLineNumberForInsertionPoint(a));180}181}, c);182}183184static int[] getRangeForLine(final Accessible a, final Component c, final int line) {185if (a == null) return null;186return CAccessibility.invokeAndWait(new Callable<int[]>() {187public int[] call() {188return getRangeForLine(a, line);189}190}, c);191}192193static int[] getRangeForIndex(final Accessible a, final Component c, final int index) {194if (a == null) return new int[2];195196return CAccessibility.invokeAndWait(new Callable<int[]>() {197public int[] call() {198final AccessibleContext ac = a.getAccessibleContext();199if (ac == null) return new int[2];200201final AccessibleEditableText aet = ac.getAccessibleEditableText();202if (aet == null) return new int[2];203204final int charCount = aet.getCharCount();205if (index >= charCount) return new int[2];206207final String foundWord = aet.getAtIndex(AccessibleText.WORD, index);208final int foundWordLength = foundWord.length();209final String wholeString = aet.getTextRange(0, charCount - 1);210211// now we need to find the index of the foundWord in wholeString. It's somewhere pretty close to the passed-in index,212// but we don't know if it's before or after the passed-in index. So, look behind and ahead of the passed-in index213int foundWordIndex = -1;214int offset = 0;215while ((foundWordIndex == -1) && (offset < foundWordLength)) {216if (wholeString.regionMatches(true, index - offset, foundWord, 0, foundWordLength)) {217// is the index of foundWord to the left of the passed-in index?218foundWordIndex = index - offset;219}220if (wholeString.regionMatches(true, index + offset, foundWord, 0, foundWordLength)) {221// is the index of the foundWord to the right of the passed-in index?222foundWordIndex = index + offset;223}224offset++;225}226227final int[] ret = new int[2];228ret[0] = foundWordIndex;229ret[1] = foundWordIndex + foundWordLength;230return ret;231}232}, c);233}234235// cmcnote: this method does not currently work for JLabels. JLabels, for some reason unbeknownst to me, do not236// return a value from getAccessibleText. According to the javadocs, AccessibleJLabels implement AccessibleText,237// so this doesn't really make sense. Perhaps a sun bug? Investigate. We currently get the text value out of labels238// via "getAccessibleName". This just returns a String - so we don't know it's position, etc, as we do for239// AccessibleText.240static double[] getBoundsForRange(final Accessible a, final Component c, final int location, final int length) {241final double[] ret = new double[4];242if (a == null) return ret;243244return CAccessibility.invokeAndWait(new Callable<double[]>() {245public double[] call() throws Exception {246final AccessibleContext ac = a.getAccessibleContext();247if (ac == null) return ret;248249final AccessibleText at = ac.getAccessibleText();250if (at == null) {251ac.getAccessibleName();252ac.getAccessibleEditableText();253return ret;254}255256final Rectangle2D boundsStart = at.getCharacterBounds(location);257final Rectangle2D boundsEnd = at.getCharacterBounds(location + length - 1);258if (boundsEnd == null || boundsStart == null) return ret;259final Rectangle2D boundsUnion = boundsStart.createUnion(boundsEnd);260if (boundsUnion.isEmpty()) return ret;261262final double localX = boundsUnion.getX();263final double localY = boundsUnion.getY();264265final Point componentLocation = ac.getAccessibleComponent().getLocationOnScreen();266if (componentLocation == null) return ret;267268final double screenX = componentLocation.getX() + localX;269final double screenY = componentLocation.getY() + localY;270271ret[0] = screenX;272ret[1] = screenY; // in java screen coords (from top-left corner of screen)273ret[2] = boundsUnion.getWidth();274ret[3] = boundsUnion.getHeight();275return ret;276}277}, c);278}279280static String getStringForRange(final Accessible a, final Component c, final int location, final int length) {281if (a == null) return null;282return CAccessibility.invokeAndWait(new Callable<String>() {283public String call() throws Exception {284final AccessibleContext ac = a.getAccessibleContext();285if (ac == null) return null;286287final AccessibleEditableText aet = ac.getAccessibleEditableText();288if (aet == null) return null;289290return aet.getTextRange(location, location + length);291}292}, c);293}294295@SuppressWarnings("deprecation")296static int[] getVisibleCharacterRange(final Accessible a) {297final Accessible sa = CAccessible.getSwingAccessible(a);298if (!(sa instanceof JTextComponent)) return null;299300final JTextComponent jc = (JTextComponent) sa;301final Rectangle rect = jc.getVisibleRect();302final Point topLeft = new Point(rect.x, rect.y);303final Point topRight = new Point(rect.x + rect.width, rect.y);304final Point bottomLeft = new Point(rect.x, rect.y + rect.height);305final Point bottomRight = new Point(rect.x + rect.width, rect.y + rect.height);306307int start = Math.min(jc.viewToModel(topLeft), jc.viewToModel(topRight));308int end = Math.max(jc.viewToModel(bottomLeft), jc.viewToModel(bottomRight));309if (start < 0) start = 0;310if (end < 0) end = 0;311return new int[] { start, end };312}313314static int getLineNumberForIndex(final Accessible a, int index) {315final Accessible sa = CAccessible.getSwingAccessible(a);316if (!(sa instanceof JTextComponent)) return -1;317318final JTextComponent jc = (JTextComponent) sa;319final Element root = jc.getDocument().getDefaultRootElement();320321// treat -1 special, returns the current caret position322if (index == -1) index = jc.getCaretPosition();323324// Determine line number (can be -1)325return root.getElementIndex(index);326}327328static int getLineNumberForInsertionPoint(final Accessible a) {329return getLineNumberForIndex(a, -1); // uses special -1 for above330}331332static int[] getRangeForLine(final Accessible a, final int lineIndex) {333Accessible sa = CAccessible.getSwingAccessible(a);334if (!(sa instanceof JTextComponent)) return null;335336final JTextComponent jc = (JTextComponent) sa;337final Element root = jc.getDocument().getDefaultRootElement();338final Element line = root.getElement(lineIndex);339if (line == null) return null;340341return new int[] { line.getStartOffset(), line.getEndOffset() };342}343}344345346