| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| package org.openoffice.java.accessibility; |
| |
| import com.sun.star.accessibility.*; |
| import com.sun.star.awt.*; |
| import com.sun.star.style.*; |
| import com.sun.star.uno.*; |
| |
| import org.openoffice.java.accessibility.logging.*; |
| |
| import java.text.BreakIterator; |
| import java.util.Locale; |
| |
| import javax.accessibility.AccessibleContext; |
| import javax.accessibility.AccessibleText; |
| |
| import javax.swing.text.StyleConstants; |
| |
| /** The GenericAccessibleEditableText mapps the calls to the java AccessibleEditableText |
| * interface to the corresponding methods of the UNO XAccessibleEditableText interface. |
| */ |
| public class AccessibleTextImpl implements javax.accessibility.AccessibleText { |
| final static double toPointFactor = 1 / ((7 / 10) + 34.5); |
| final static String[] attributeList = { |
| "ParaAdjust", "CharBackColor", "CharWeight", "ParaFirstLineIndent", |
| "CharFontPitch", "CharHeight", "CharColor", "CharPosture", |
| "ParaLeftMargin", "ParaLineSpacing", "ParaTopMargin", "ParaBottomMargin", |
| "CharStrikeout", "CharEscapement", "ParaTabStops", "CharUnderline" |
| }; |
| |
| final static String[] localeAttributeList = { |
| "CharLocale", "CharLocaleAsian", "CharLocaleComplex" |
| }; |
| |
| XAccessibleText unoObject; |
| private javax.swing.text.TabSet tabSet = null; |
| private javax.swing.text.TabStop[] tabStops = null; |
| private static Type TextSegmentType = new Type(TextSegment.class); |
| private static Type UnoLocaleType = new Type(com.sun.star.lang.Locale.class); |
| |
| /** Creates new GenericAccessibleEditableText object */ |
| public AccessibleTextImpl(XAccessibleText xAccessibleText) { |
| |
| if (Build.PRODUCT) { |
| unoObject = xAccessibleText; |
| } else { |
| String property = System.getProperty("AccessBridgeLogging"); |
| if ((property != null) && (property.indexOf("text") != -1)) { |
| unoObject = new XAccessibleTextLog(xAccessibleText); |
| } else { |
| unoObject = xAccessibleText; |
| } |
| } |
| } |
| |
| public AccessibleTextImpl() { |
| } |
| |
| public static javax.accessibility.AccessibleText get(com.sun.star.uno.XInterface unoObject) { |
| try { |
| XAccessibleText unoAccessibleText = (XAccessibleText) |
| UnoRuntime.queryInterface(XAccessibleText.class, unoObject); |
| if (unoAccessibleText != null) { |
| return new AccessibleTextImpl(unoAccessibleText); |
| } |
| } catch (com.sun.star.uno.RuntimeException e) { |
| } |
| return null; |
| } |
| |
| protected static Object convertTextSegment(Object any) { |
| try { |
| if (AnyConverter.isObject(any)) { |
| TextSegment ts = (TextSegment) |
| AnyConverter.toObject(TextSegmentType, any); |
| if (ts != null) { |
| // Since there is nothing like a "range" object in the JAA yet, |
| // the Object[3] is a private negotiation with the JABG |
| Object[] array = { new Integer(ts.SegmentStart), |
| new Integer(ts.SegmentEnd), ts.SegmentText }; |
| return array; |
| } |
| } |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| } |
| |
| return null; |
| } |
| |
| /** Returns the locale object. |
| * |
| * Since switching the UI language only takes effect on the next |
| * office start, UI elements can return a cached value here - given |
| * that Java UNO initializes the default locale correctly, this is |
| * the perfect place to grab this cached values. |
| * |
| * However, since there are more sophisticated components with |
| * potentially more than one locale, we first check for the |
| * CharLocale[Asian|Complex] property. |
| */ |
| |
| protected java.util.Locale getLocale(int index) { |
| try { |
| com.sun.star.beans.PropertyValue[] propertyValues = |
| unoObject.getCharacterAttributes(index, localeAttributeList); |
| |
| if (null != propertyValues) { |
| for (int i = 0; i < propertyValues.length; i++) { |
| com.sun.star.lang.Locale unoLocale = (com.sun.star.lang.Locale) |
| AnyConverter.toObject(UnoLocaleType, propertyValues[i]); |
| if (unoLocale != null) { |
| return new java.util.Locale(unoLocale.Language, unoLocale.Country); |
| } |
| } |
| } |
| |
| return java.util.Locale.getDefault(); |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| return java.util.Locale.getDefault(); |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| return java.util.Locale.getDefault(); |
| } |
| } |
| |
| |
| /** Returns the string after a given index |
| * |
| * The Java word iterator has a different understanding of what |
| * a word is than the word iterator used by OOo, so we use the |
| * Java iterators to ensure maximal compatibility with Java. |
| */ |
| public String getAfterIndex(int part, int index) { |
| switch (part) { |
| case AccessibleText.CHARACTER: |
| try { |
| String s = unoObject.getText(); |
| return s.substring(index+1, index+2); |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case AccessibleText.WORD: |
| try { |
| String s = unoObject.getText(); |
| BreakIterator words = BreakIterator.getWordInstance(getLocale(index)); |
| words.setText(s); |
| int start = words.following(index); |
| if (start == BreakIterator.DONE || start >= s.length()) { |
| return null; |
| } |
| int end = words.following(start); |
| if (end == BreakIterator.DONE || end >= s.length()) { |
| return null; |
| } |
| return s.substring(start, end); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case AccessibleText.SENTENCE: |
| try { |
| String s = unoObject.getText(); |
| BreakIterator sentence = |
| BreakIterator.getSentenceInstance(getLocale(index)); |
| sentence.setText(s); |
| int start = sentence.following(index); |
| if (start == BreakIterator.DONE || start >= s.length()) { |
| return null; |
| } |
| int end = sentence.following(start); |
| if (end == BreakIterator.DONE || end >= s.length()) { |
| return null; |
| } |
| return s.substring(start, end); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case 4: |
| try { |
| TextSegment ts = unoObject.getTextBehindIndex(index, AccessibleTextType.LINE); |
| return ts.SegmentText; |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| // Workaround for #104847# |
| if (index > 0 && getCharCount() == index) { |
| return getAfterIndex(part, index - 1); |
| } |
| return null; |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| return null; |
| } |
| case 5: |
| try { |
| TextSegment ts = unoObject.getTextBehindIndex(index, AccessibleTextType.ATTRIBUTE_RUN); |
| return ts.SegmentText; |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| return null; |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| return null; |
| } |
| default: |
| return null; |
| } |
| } |
| |
| /** Returns the zero-based offset of the caret */ |
| public int getCaretPosition() { |
| try { |
| return unoObject.getCaretPosition(); |
| } catch (com.sun.star.uno.RuntimeException e) { |
| return -1; |
| } |
| } |
| |
| /** Returns the start offset within the selected text */ |
| public int getSelectionStart() { |
| try { |
| int index = unoObject.getSelectionStart(); |
| |
| if (index == -1) { |
| index = getCaretPosition(); |
| } |
| |
| return index; |
| } catch (com.sun.star.uno.RuntimeException e) { |
| return -1; |
| } |
| } |
| |
| protected void setAttribute(javax.swing.text.MutableAttributeSet as, |
| com.sun.star.beans.PropertyValue property) { |
| try { |
| // Map alignment attribute |
| if (property.Name.equals("ParaAdjust")) { |
| ParagraphAdjust adjust = null; |
| |
| if (property.Value instanceof ParagraphAdjust) { |
| adjust = (ParagraphAdjust) property.Value; |
| } else if (property.Value instanceof Any) { |
| adjust = (ParagraphAdjust) AnyConverter.toObject(new Type( |
| ParagraphAdjust.class), property.Value); |
| } else { |
| adjust = ParagraphAdjust.fromInt(AnyConverter.toInt( |
| property.Value)); |
| } |
| |
| if (adjust != null) { |
| if (adjust.equals(ParagraphAdjust.LEFT)) { |
| StyleConstants.setAlignment(as, |
| StyleConstants.ALIGN_LEFT); |
| } else if (adjust.equals(ParagraphAdjust.RIGHT)) { |
| StyleConstants.setAlignment(as, |
| StyleConstants.ALIGN_RIGHT); |
| } else if (adjust.equals(ParagraphAdjust.CENTER)) { |
| StyleConstants.setAlignment(as, |
| StyleConstants.ALIGN_CENTER); |
| } else if (adjust.equals(ParagraphAdjust.BLOCK) || |
| adjust.equals(ParagraphAdjust.STRETCH)) { |
| StyleConstants.setAlignment(as, |
| StyleConstants.ALIGN_JUSTIFIED); |
| } |
| } else if (Build.DEBUG) { |
| System.err.println( |
| "Invalid property value for key ParaAdjust: " + |
| property.Value.getClass().getName()); |
| } |
| |
| // Map background color |
| } else if (property.Name.equals("CharBackColor")) { |
| StyleConstants.setBackground(as, |
| new java.awt.Color(AnyConverter.toInt(property.Value))); |
| |
| // FIXME: BidiLevel |
| // Set bold attribute |
| } else if (property.Name.equals("CharWeight")) { |
| boolean isBold = AnyConverter.toFloat(property.Value) > 125; |
| StyleConstants.setBold(as, isBold); |
| |
| // FIXME: Java 1.4 ComponentAttribute, ComponentElementName, ComposedTextAttribute |
| // Set FirstLineIndent attribute |
| } else if (property.Name.equals("ParaFirstLineIndent")) { |
| StyleConstants.setFirstLineIndent(as, |
| (float) (toPointFactor * AnyConverter.toInt(property.Value))); |
| |
| // Set font family attribute |
| } else if (property.Name.equals("CharFontPitch")) { |
| if (AnyConverter.toShort(property.Value) == 2) { |
| StyleConstants.setFontFamily(as, "Proportional"); |
| } |
| |
| // Set font size attribute |
| } else if (property.Name.equals("CharHeight")) { |
| StyleConstants.setFontSize(as, |
| (int) AnyConverter.toFloat(property.Value)); |
| |
| // Map foreground color |
| } else if (property.Name.equals("CharColor")) { |
| StyleConstants.setForeground(as, |
| new java.awt.Color(AnyConverter.toInt(property.Value))); |
| |
| // FIXME: IconAttribute, IconElementName |
| // Set italic attribute |
| } else if (property.Name.equals("CharPosture")) { |
| FontSlant fs = null; |
| |
| if (property.Value instanceof FontSlant) { |
| fs = (FontSlant) property.Value; |
| } else if (property.Value instanceof Any) { |
| fs = (FontSlant) AnyConverter.toObject(new Type( |
| FontSlant.class), property.Value); |
| } |
| |
| if (fs != null) { |
| StyleConstants.setItalic(as, FontSlant.ITALIC.equals(fs)); |
| } |
| |
| // Set left indent attribute |
| } else if (property.Name.equals("ParaLeftMargin")) { |
| StyleConstants.setLeftIndent(as, |
| (float) (toPointFactor * AnyConverter.toInt(property.Value))); |
| |
| // Set right indent attribute |
| } else if (property.Name.equals("ParaRightMargin")) { |
| StyleConstants.setRightIndent(as, |
| (float) (toPointFactor * AnyConverter.toInt(property.Value))); |
| } |
| // Set line spacing attribute |
| else if (property.Name.equals("ParaLineSpacing")) { |
| LineSpacing ls = null; |
| |
| if (property.Value instanceof LineSpacing) { |
| ls = (LineSpacing) property.Value; |
| } else if (property.Value instanceof Any) { |
| ls = (LineSpacing) AnyConverter.toObject(new Type( |
| LineSpacing.class), property.Value); |
| } |
| |
| if (ls != null) { |
| StyleConstants.setLineSpacing(as, |
| (float) (toPointFactor * ls.Height)); |
| } |
| } |
| // FIXME: Java 1.4 NameAttribute, Orientation, ResolveAttribute |
| // Set space above attribute |
| else if (property.Name.equals("ParaTopMargin")) { |
| StyleConstants.setSpaceAbove(as, |
| (float) (toPointFactor * AnyConverter.toInt(property.Value))); |
| } |
| // Set space below attribute |
| else if (property.Name.equals("ParaBottomMargin")) { |
| StyleConstants.setSpaceBelow(as, |
| (float) (toPointFactor * AnyConverter.toInt(property.Value))); |
| |
| // Set strike through attribute |
| } else if (property.Name.equals("CharStrikeout")) { |
| boolean isStrikeThrough = (FontStrikeout.NONE != AnyConverter.toShort(property.Value)); |
| StyleConstants.setStrikeThrough(as, isStrikeThrough); |
| |
| // Set sub-/superscript attribute |
| } else if (property.Name.equals("CharEscapement")) { |
| short value = AnyConverter.toShort(property.Value); |
| |
| if (value > 0) { |
| StyleConstants.setSuperscript(as, true); |
| } else if (value < 0) { |
| StyleConstants.setSubscript(as, true); |
| } |
| |
| // Set tabset attribute |
| } else if (property.Name.equals("ParaTabStops")) { |
| TabStop[] unoTabStops = (TabStop[]) AnyConverter.toArray(property.Value); |
| javax.swing.text.TabStop[] tabStops = new javax.swing.text.TabStop[unoTabStops.length]; |
| |
| for (int index2 = 0; index2 < unoTabStops.length; index2++) { |
| float pos = (float) (toPointFactor * unoTabStops[index2].Position); |
| |
| if (unoTabStops[index2].Alignment.equals(TabAlign.LEFT)) { |
| tabStops[index2] = new javax.swing.text.TabStop(pos, |
| javax.swing.text.TabStop.ALIGN_LEFT, |
| javax.swing.text.TabStop.LEAD_NONE); |
| } else if (unoTabStops[index2].Alignment.equals( |
| TabAlign.CENTER)) { |
| tabStops[index2] = new javax.swing.text.TabStop(pos, |
| javax.swing.text.TabStop.ALIGN_CENTER, |
| javax.swing.text.TabStop.LEAD_NONE); |
| } else if (unoTabStops[index2].Alignment.equals( |
| TabAlign.RIGHT)) { |
| tabStops[index2] = new javax.swing.text.TabStop(pos, |
| javax.swing.text.TabStop.ALIGN_RIGHT, |
| javax.swing.text.TabStop.LEAD_NONE); |
| } else if (unoTabStops[index2].Alignment.equals( |
| TabAlign.DECIMAL)) { |
| tabStops[index2] = new javax.swing.text.TabStop(pos, |
| javax.swing.text.TabStop.ALIGN_DECIMAL, |
| javax.swing.text.TabStop.LEAD_NONE); |
| } else { |
| tabStops[index2] = new javax.swing.text.TabStop(pos); |
| } |
| } |
| |
| // Re-use tabSet object if possible to make AttributeSet.equals work |
| if ((this.tabSet == null) || |
| !java.util.Arrays.equals(tabStops, this.tabStops)) { |
| this.tabStops = tabStops; |
| this.tabSet = new javax.swing.text.TabSet(tabStops); |
| } |
| |
| StyleConstants.setTabSet(as, this.tabSet); |
| |
| // Set underline attribute |
| } else if (property.Name.equals("CharUnderline")) { |
| boolean isUnderline = (FontUnderline.NONE != AnyConverter.toShort(property.Value)); |
| StyleConstants.setUnderline(as, isUnderline); |
| } |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| if (Build.DEBUG) { |
| System.err.println("*** ERROR *** " + e.getClass().getName() + |
| " caught for property " + property.Name + ": " + |
| e.getMessage()); |
| System.err.println(" value is of type " + |
| property.Value.getClass().getName()); |
| } |
| } |
| } |
| |
| /** Returns the AttributSet for a given character at a given index */ |
| public javax.swing.text.AttributeSet getCharacterAttribute(int index) { |
| try { |
| com.sun.star.beans.PropertyValue[] propertyValues = unoObject.getCharacterAttributes(index, |
| attributeList); |
| javax.swing.text.SimpleAttributeSet attributeSet = new javax.swing.text.SimpleAttributeSet(); |
| |
| if (null != propertyValues) { |
| for (int i = 0; i < propertyValues.length; i++) { |
| setAttribute(attributeSet, propertyValues[i]); |
| } |
| } |
| |
| return attributeSet; |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| if ((index > 0) && (getCharCount() == index)) { |
| return getCharacterAttribute(index - 1); |
| } |
| return null; |
| } |
| } |
| |
| /** Given a point in local coordinates, return the zero-based index of the character under that point */ |
| public int getIndexAtPoint(java.awt.Point point) { |
| try { |
| return unoObject.getIndexAtPoint(new Point(point.x, point.y)); |
| } catch (com.sun.star.uno.RuntimeException e) { |
| return -1; |
| } |
| } |
| |
| /** Returns the end offset within the selected text */ |
| public int getSelectionEnd() { |
| try { |
| int index = unoObject.getSelectionEnd(); |
| |
| if (index == -1) { |
| index = getCaretPosition(); |
| } |
| |
| return index; |
| } catch (com.sun.star.uno.RuntimeException e) { |
| return -1; |
| } |
| } |
| |
| /** Returns the string before a given index |
| * |
| * The Java word iterator has a different understanding of what |
| * a word is than the word iterator used by OOo, so we use the |
| * Java iterators to ensure maximal compatibility with Java. |
| */ |
| public java.lang.String getBeforeIndex(int part, int index) { |
| switch (part) { |
| case AccessibleText.CHARACTER: |
| try { |
| String s = unoObject.getText(); |
| return s.substring(index-1, index); |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case AccessibleText.WORD: |
| try { |
| String s = unoObject.getText(); |
| BreakIterator words = BreakIterator.getWordInstance(getLocale(index)); |
| words.setText(s); |
| int end = words.following(index); |
| end = words.previous(); |
| int start = words.previous(); |
| if (start == BreakIterator.DONE) { |
| return null; |
| } |
| return s.substring(start, end); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case AccessibleText.SENTENCE: |
| try { |
| String s = unoObject.getText(); |
| BreakIterator sentence = |
| BreakIterator.getSentenceInstance(getLocale(index)); |
| sentence.setText(s); |
| int end = sentence.following(index); |
| end = sentence.previous(); |
| int start = sentence.previous(); |
| if (start == BreakIterator.DONE) { |
| return null; |
| } |
| return s.substring(start, end); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case 4: |
| try { |
| TextSegment ts = unoObject.getTextBeforeIndex(index, AccessibleTextType.LINE); |
| return ts.SegmentText; |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| // Workaround for #104847# |
| if (index > 0 && getCharCount() == index) { |
| return getBeforeIndex(part, index - 1); |
| } |
| return null; |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| return null; |
| } |
| case 5: |
| try { |
| TextSegment ts = unoObject.getTextBeforeIndex(index, AccessibleTextType.ATTRIBUTE_RUN); |
| return ts.SegmentText; |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| return null; |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| return null; |
| } |
| default: |
| return null; |
| } |
| } |
| |
| |
| /** Returns the string at a given index |
| * |
| * The Java word iterator has a different understanding of what |
| * a word is than the word iterator used by OOo, so we use the |
| * Java iterators to ensure maximal compatibility with Java. |
| */ |
| public java.lang.String getAtIndex(int part, int index) { |
| switch (part) { |
| case AccessibleText.CHARACTER: |
| try { |
| String s = unoObject.getText(); |
| return s.substring(index, index + 1); |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case AccessibleText.WORD: |
| try { |
| String s = unoObject.getText(); |
| BreakIterator words = BreakIterator.getWordInstance(getLocale(index)); |
| words.setText(s); |
| int end = words.following(index); |
| return s.substring(words.previous(), end); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case AccessibleText.SENTENCE: |
| try { |
| String s = unoObject.getText(); |
| BreakIterator sentence = |
| BreakIterator.getSentenceInstance(getLocale(index)); |
| sentence.setText(s); |
| int end = sentence.following(index); |
| return s.substring(sentence.previous(), end); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } catch (IndexOutOfBoundsException e) { |
| return null; |
| } |
| case 4: |
| try { |
| TextSegment ts = unoObject.getTextAtIndex(index, AccessibleTextType.LINE); |
| return ts.SegmentText; |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| // Workaround for #104847# |
| if (index > 0 && getCharCount() == index) { |
| return getAtIndex(part, index - 1); |
| } |
| return null; |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| return null; |
| } |
| case 5: |
| try { |
| TextSegment ts = unoObject.getTextAtIndex(index, AccessibleTextType.ATTRIBUTE_RUN); |
| return ts.SegmentText; |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| return null; |
| } catch (com.sun.star.lang.IllegalArgumentException e) { |
| return null; |
| } |
| |
| default: |
| return null; |
| } |
| } |
| |
| /** Returns the number of characters (valid indicies) */ |
| public int getCharCount() { |
| try { |
| return unoObject.getCharacterCount(); |
| } catch (com.sun.star.uno.RuntimeException e) { |
| } |
| |
| return 0; |
| } |
| |
| /** Returns the portion of the text that is selected */ |
| public java.lang.String getSelectedText() { |
| try { |
| return unoObject.getSelectedText(); |
| } catch (com.sun.star.uno.RuntimeException e) { |
| } |
| |
| return null; |
| } |
| |
| /** Determines the bounding box of the character at the given index into the string */ |
| public java.awt.Rectangle getCharacterBounds(int index) { |
| try { |
| Rectangle unoRect = unoObject.getCharacterBounds(index); |
| return new java.awt.Rectangle(unoRect.X, unoRect.Y, unoRect.Width, unoRect.Height); |
| } catch (com.sun.star.lang.IndexOutOfBoundsException e) { |
| if ((index > 0) && (getCharCount() == index)) { |
| return getCharacterBounds(index - 1); |
| } |
| } catch (com.sun.star.uno.RuntimeException e) { |
| } |
| |
| return new java.awt.Rectangle(); |
| } |
| } |