1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package org.openoffice.java.accessibility;
24 
25 import com.sun.star.accessibility.*;
26 import com.sun.star.awt.*;
27 import com.sun.star.style.*;
28 import com.sun.star.uno.*;
29 
30 import org.openoffice.java.accessibility.logging.*;
31 
32 import java.text.BreakIterator;
33 import java.util.Locale;
34 
35 import javax.accessibility.AccessibleContext;
36 import javax.accessibility.AccessibleText;
37 
38 import javax.swing.text.StyleConstants;
39 
40 /** The GenericAccessibleEditableText mapps the calls to the java AccessibleEditableText
41  *  interface to the corresponding methods of the UNO XAccessibleEditableText interface.
42  */
43 public class AccessibleTextImpl implements javax.accessibility.AccessibleText {
44     final static double toPointFactor = 1 / ((7 / 10) + 34.5);
45     final static String[] attributeList = {
46         "ParaAdjust", "CharBackColor", "CharWeight", "ParaFirstLineIndent",
47         "CharFontPitch", "CharHeight", "CharColor", "CharPosture",
48         "ParaLeftMargin", "ParaLineSpacing", "ParaTopMargin", "ParaBottomMargin",
49         "CharStrikeout", "CharEscapement", "ParaTabStops", "CharUnderline"
50     };
51 
52     final static String[] localeAttributeList = {
53         "CharLocale", "CharLocaleAsian", "CharLocaleComplex"
54     };
55 
56     XAccessibleText unoObject;
57     private javax.swing.text.TabSet tabSet = null;
58     private javax.swing.text.TabStop[] tabStops = null;
59     private static Type TextSegmentType = new Type(TextSegment.class);
60     private static Type UnoLocaleType = new Type(com.sun.star.lang.Locale.class);
61 
62     /** Creates new GenericAccessibleEditableText object */
AccessibleTextImpl(XAccessibleText xAccessibleText)63     public AccessibleTextImpl(XAccessibleText xAccessibleText) {
64 
65         if (Build.PRODUCT) {
66             unoObject = xAccessibleText;
67         } else {
68             String property = System.getProperty("AccessBridgeLogging");
69             if ((property != null) && (property.indexOf("text") != -1)) {
70                 unoObject = new XAccessibleTextLog(xAccessibleText);
71             } else {
72                 unoObject = xAccessibleText;
73             }
74         }
75     }
76 
AccessibleTextImpl()77     public AccessibleTextImpl() {
78     }
79 
get(com.sun.star.uno.XInterface unoObject)80     public static javax.accessibility.AccessibleText get(com.sun.star.uno.XInterface unoObject) {
81         try {
82             XAccessibleText unoAccessibleText = (XAccessibleText)
83                 UnoRuntime.queryInterface(XAccessibleText.class, unoObject);
84             if (unoAccessibleText != null) {
85                 return new AccessibleTextImpl(unoAccessibleText);
86             }
87         } catch (com.sun.star.uno.RuntimeException e) {
88         }
89         return null;
90     }
91 
convertTextSegment(Object any)92     protected static Object convertTextSegment(Object any) {
93         try {
94             if (AnyConverter.isObject(any)) {
95                 TextSegment ts = (TextSegment)
96                     AnyConverter.toObject(TextSegmentType, any);
97                 if (ts != null) {
98                     // Since there is nothing like a "range" object in the JAA yet,
99                     // the Object[3] is a private negotiation with the JABG
100                     Object[] array = { new Integer(ts.SegmentStart),
101                         new Integer(ts.SegmentEnd), ts.SegmentText };
102                     return array;
103                 }
104             }
105         } catch (com.sun.star.lang.IllegalArgumentException e) {
106         }
107 
108         return null;
109     }
110 
111     /** Returns the locale object.
112      *
113      *  Since switching the UI language only takes effect on the next
114      *  office start, UI elements can return a cached value here - given
115      *  that Java UNO initializes the default locale correctly, this is
116      *  the perfect place to grab this cached values.
117      *
118      *  However, since there are more sophisticated components with
119      *  potentially more than one locale, we first check for the
120      *  CharLocale[Asian|Complex] property.
121      */
122 
getLocale(int index)123     protected java.util.Locale getLocale(int index) {
124         try {
125             com.sun.star.beans.PropertyValue[] propertyValues =
126                 unoObject.getCharacterAttributes(index, localeAttributeList);
127 
128             if (null != propertyValues) {
129                 for (int i = 0; i < propertyValues.length; i++) {
130                     com.sun.star.lang.Locale unoLocale = (com.sun.star.lang.Locale)
131                         AnyConverter.toObject(UnoLocaleType, propertyValues[i]);
132                     if (unoLocale != null) {
133                         return new java.util.Locale(unoLocale.Language, unoLocale.Country);
134                     }
135                 }
136             }
137 
138             return java.util.Locale.getDefault();
139         } catch (com.sun.star.lang.IllegalArgumentException e) {
140             return java.util.Locale.getDefault();
141         } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
142             return java.util.Locale.getDefault();
143         }
144     }
145 
146 
147     /** Returns the string after a given index
148      *
149      *  The Java word iterator has a different understanding of what
150      *  a word is than the word iterator used by OOo, so we use the
151      *  Java iterators to ensure maximal compatibility with Java.
152      */
getAfterIndex(int part, int index)153     public String getAfterIndex(int part, int index) {
154         switch (part) {
155         case AccessibleText.CHARACTER:
156             try {
157                 String s = unoObject.getText();
158                 return s.substring(index+1, index+2);
159             } catch (IndexOutOfBoundsException e) {
160                 return null;
161             }
162         case AccessibleText.WORD:
163             try {
164                 String s = unoObject.getText();
165                 BreakIterator words = BreakIterator.getWordInstance(getLocale(index));
166                 words.setText(s);
167                 int start = words.following(index);
168                 if (start == BreakIterator.DONE || start >= s.length()) {
169                     return null;
170                 }
171                 int end = words.following(start);
172                 if (end == BreakIterator.DONE || end >= s.length()) {
173                     return null;
174                 }
175                 return s.substring(start, end);
176             } catch (IllegalArgumentException e) {
177                 return null;
178             } catch (IndexOutOfBoundsException e) {
179                 return null;
180             }
181         case AccessibleText.SENTENCE:
182             try {
183                 String s = unoObject.getText();
184                 BreakIterator sentence =
185                     BreakIterator.getSentenceInstance(getLocale(index));
186                 sentence.setText(s);
187                 int start = sentence.following(index);
188                 if (start == BreakIterator.DONE || start >= s.length()) {
189                     return null;
190                 }
191                 int end = sentence.following(start);
192                 if (end == BreakIterator.DONE || end >= s.length()) {
193                     return null;
194                 }
195                 return s.substring(start, end);
196             } catch (IllegalArgumentException e) {
197                 return null;
198             } catch (IndexOutOfBoundsException e) {
199                 return null;
200             }
201         case 4:
202             try {
203                 TextSegment ts = unoObject.getTextBehindIndex(index, AccessibleTextType.LINE);
204                 return ts.SegmentText;
205             } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
206                 // Workaround for #104847#
207                 if (index > 0 && getCharCount() == index) {
208                     return getAfterIndex(part, index - 1);
209                 }
210                 return null;
211             } catch (com.sun.star.lang.IllegalArgumentException e) {
212                 return null;
213             }
214         case 5:
215             try {
216                 TextSegment ts = unoObject.getTextBehindIndex(index, AccessibleTextType.ATTRIBUTE_RUN);
217                 return ts.SegmentText;
218             } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
219                 return null;
220             } catch (com.sun.star.lang.IllegalArgumentException e) {
221                 return null;
222             }
223         default:
224             return null;
225         }
226     }
227 
228     /** Returns the zero-based offset of the caret */
getCaretPosition()229     public int getCaretPosition() {
230         try {
231             return unoObject.getCaretPosition();
232         } catch (com.sun.star.uno.RuntimeException e) {
233             return -1;
234         }
235     }
236 
237     /** Returns the start offset within the selected text */
getSelectionStart()238     public int getSelectionStart() {
239         try {
240             int index = unoObject.getSelectionStart();
241 
242             if (index == -1) {
243                 index = getCaretPosition();
244             }
245 
246             return index;
247         } catch (com.sun.star.uno.RuntimeException e) {
248             return -1;
249         }
250     }
251 
setAttribute(javax.swing.text.MutableAttributeSet as, com.sun.star.beans.PropertyValue property)252     protected void setAttribute(javax.swing.text.MutableAttributeSet as,
253         com.sun.star.beans.PropertyValue property) {
254         try {
255             // Map alignment attribute
256             if (property.Name.equals("ParaAdjust")) {
257                 ParagraphAdjust adjust = null;
258 
259                 if (property.Value instanceof ParagraphAdjust) {
260                     adjust = (ParagraphAdjust) property.Value;
261                 } else if (property.Value instanceof Any) {
262                     adjust = (ParagraphAdjust) AnyConverter.toObject(new Type(
263                                 ParagraphAdjust.class), property.Value);
264                 } else {
265                     adjust = ParagraphAdjust.fromInt(AnyConverter.toInt(
266                                 property.Value));
267                 }
268 
269                 if (adjust != null) {
270                     if (adjust.equals(ParagraphAdjust.LEFT)) {
271                         StyleConstants.setAlignment(as,
272                             StyleConstants.ALIGN_LEFT);
273                     } else if (adjust.equals(ParagraphAdjust.RIGHT)) {
274                         StyleConstants.setAlignment(as,
275                             StyleConstants.ALIGN_RIGHT);
276                     } else if (adjust.equals(ParagraphAdjust.CENTER)) {
277                         StyleConstants.setAlignment(as,
278                             StyleConstants.ALIGN_CENTER);
279                     } else if (adjust.equals(ParagraphAdjust.BLOCK) ||
280                             adjust.equals(ParagraphAdjust.STRETCH)) {
281                         StyleConstants.setAlignment(as,
282                             StyleConstants.ALIGN_JUSTIFIED);
283                     }
284                 } else if (Build.DEBUG) {
285                     System.err.println(
286                         "Invalid property value for key ParaAdjust: " +
287                         property.Value.getClass().getName());
288                 }
289 
290                 // Map background color
291             } else if (property.Name.equals("CharBackColor")) {
292                 StyleConstants.setBackground(as,
293                     new java.awt.Color(AnyConverter.toInt(property.Value)));
294 
295                 // FIXME: BidiLevel
296                 // Set bold attribute
297             } else if (property.Name.equals("CharWeight")) {
298                 boolean isBold = AnyConverter.toFloat(property.Value) > 125;
299                 StyleConstants.setBold(as, isBold);
300 
301                 // FIXME: Java 1.4 ComponentAttribute, ComponentElementName, ComposedTextAttribute
302                 // Set FirstLineIndent attribute
303             } else if (property.Name.equals("ParaFirstLineIndent")) {
304                 StyleConstants.setFirstLineIndent(as,
305                     (float) (toPointFactor * AnyConverter.toInt(property.Value)));
306 
307                 // Set font family attribute
308             } else if (property.Name.equals("CharFontPitch")) {
309                 if (AnyConverter.toShort(property.Value) == 2) {
310                     StyleConstants.setFontFamily(as, "Proportional");
311                 }
312 
313                 // Set font size attribute
314             } else if (property.Name.equals("CharHeight")) {
315                 StyleConstants.setFontSize(as,
316                     (int) AnyConverter.toFloat(property.Value));
317 
318                 // Map foreground color
319             } else if (property.Name.equals("CharColor")) {
320                 StyleConstants.setForeground(as,
321                     new java.awt.Color(AnyConverter.toInt(property.Value)));
322 
323                 // FIXME: IconAttribute, IconElementName
324                 // Set italic attribute
325             } else if (property.Name.equals("CharPosture")) {
326                 FontSlant fs = null;
327 
328                 if (property.Value instanceof FontSlant) {
329                     fs = (FontSlant) property.Value;
330                 } else if (property.Value instanceof Any) {
331                     fs = (FontSlant) AnyConverter.toObject(new Type(
332                                 FontSlant.class), property.Value);
333                 }
334 
335                 if (fs != null) {
336                     StyleConstants.setItalic(as, FontSlant.ITALIC.equals(fs));
337                 }
338 
339                 // Set left indent attribute
340             } else if (property.Name.equals("ParaLeftMargin")) {
341                 StyleConstants.setLeftIndent(as,
342                     (float) (toPointFactor * AnyConverter.toInt(property.Value)));
343 
344                 // Set right indent attribute
345             } else if (property.Name.equals("ParaRightMargin")) {
346                 StyleConstants.setRightIndent(as,
347                     (float) (toPointFactor * AnyConverter.toInt(property.Value)));
348             }
349             // Set line spacing attribute
350             else if (property.Name.equals("ParaLineSpacing")) {
351                 LineSpacing ls = null;
352 
353                 if (property.Value instanceof LineSpacing) {
354                     ls = (LineSpacing) property.Value;
355                 } else if (property.Value instanceof Any) {
356                     ls = (LineSpacing) AnyConverter.toObject(new Type(
357                                 LineSpacing.class), property.Value);
358                 }
359 
360                 if (ls != null) {
361                     StyleConstants.setLineSpacing(as,
362                         (float) (toPointFactor * ls.Height));
363                 }
364             }
365             // FIXME: Java 1.4 NameAttribute, Orientation, ResolveAttribute
366             // Set space above attribute
367             else if (property.Name.equals("ParaTopMargin")) {
368                 StyleConstants.setSpaceAbove(as,
369                     (float) (toPointFactor * AnyConverter.toInt(property.Value)));
370             }
371             // Set space below attribute
372             else if (property.Name.equals("ParaBottomMargin")) {
373                 StyleConstants.setSpaceBelow(as,
374                     (float) (toPointFactor * AnyConverter.toInt(property.Value)));
375 
376                 // Set strike through attribute
377             } else if (property.Name.equals("CharStrikeout")) {
378                 boolean isStrikeThrough = (FontStrikeout.NONE != AnyConverter.toShort(property.Value));
379                 StyleConstants.setStrikeThrough(as, isStrikeThrough);
380 
381                 // Set sub-/superscript attribute
382             } else if (property.Name.equals("CharEscapement")) {
383                 short value = AnyConverter.toShort(property.Value);
384 
385                 if (value > 0) {
386                     StyleConstants.setSuperscript(as, true);
387                 } else if (value < 0) {
388                     StyleConstants.setSubscript(as, true);
389                 }
390 
391                 // Set tabset attribute
392             } else if (property.Name.equals("ParaTabStops")) {
393                 TabStop[] unoTabStops = (TabStop[]) AnyConverter.toArray(property.Value);
394                 javax.swing.text.TabStop[] tabStops = new javax.swing.text.TabStop[unoTabStops.length];
395 
396                 for (int index2 = 0; index2 < unoTabStops.length; index2++) {
397                     float pos = (float) (toPointFactor * unoTabStops[index2].Position);
398 
399                     if (unoTabStops[index2].Alignment.equals(TabAlign.LEFT)) {
400                         tabStops[index2] = new javax.swing.text.TabStop(pos,
401                                 javax.swing.text.TabStop.ALIGN_LEFT,
402                                 javax.swing.text.TabStop.LEAD_NONE);
403                     } else if (unoTabStops[index2].Alignment.equals(
404                                 TabAlign.CENTER)) {
405                         tabStops[index2] = new javax.swing.text.TabStop(pos,
406                                 javax.swing.text.TabStop.ALIGN_CENTER,
407                                 javax.swing.text.TabStop.LEAD_NONE);
408                     } else if (unoTabStops[index2].Alignment.equals(
409                                 TabAlign.RIGHT)) {
410                         tabStops[index2] = new javax.swing.text.TabStop(pos,
411                                 javax.swing.text.TabStop.ALIGN_RIGHT,
412                                 javax.swing.text.TabStop.LEAD_NONE);
413                     } else if (unoTabStops[index2].Alignment.equals(
414                                 TabAlign.DECIMAL)) {
415                         tabStops[index2] = new javax.swing.text.TabStop(pos,
416                                 javax.swing.text.TabStop.ALIGN_DECIMAL,
417                                 javax.swing.text.TabStop.LEAD_NONE);
418                     } else {
419                         tabStops[index2] = new javax.swing.text.TabStop(pos);
420                     }
421                 }
422 
423                 // Re-use tabSet object if possible to make AttributeSet.equals work
424                 if ((this.tabSet == null) ||
425                         !java.util.Arrays.equals(tabStops, this.tabStops)) {
426                     this.tabStops = tabStops;
427                     this.tabSet = new javax.swing.text.TabSet(tabStops);
428                 }
429 
430                 StyleConstants.setTabSet(as, this.tabSet);
431 
432                 // Set underline attribute
433             } else if (property.Name.equals("CharUnderline")) {
434                 boolean isUnderline = (FontUnderline.NONE != AnyConverter.toShort(property.Value));
435                 StyleConstants.setUnderline(as, isUnderline);
436             }
437         } catch (com.sun.star.lang.IllegalArgumentException e) {
438             if (Build.DEBUG) {
439                 System.err.println("*** ERROR *** " + e.getClass().getName() +
440                     " caught for property " + property.Name + ": " +
441                     e.getMessage());
442                 System.err.println("              value is of type " +
443                     property.Value.getClass().getName());
444             }
445         }
446     }
447 
448     /** Returns the AttributSet for a given character at a given index */
getCharacterAttribute(int index)449     public javax.swing.text.AttributeSet getCharacterAttribute(int index) {
450         try {
451             com.sun.star.beans.PropertyValue[] propertyValues = unoObject.getCharacterAttributes(index,
452                     attributeList);
453             javax.swing.text.SimpleAttributeSet attributeSet = new javax.swing.text.SimpleAttributeSet();
454 
455             if (null != propertyValues) {
456                 for (int i = 0; i < propertyValues.length; i++) {
457                     setAttribute(attributeSet, propertyValues[i]);
458                 }
459             }
460 
461             return attributeSet;
462         } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
463             if ((index > 0) && (getCharCount() == index)) {
464                 return getCharacterAttribute(index - 1);
465             }
466             return null;
467         }
468     }
469 
470     /** Given a point in local coordinates, return the zero-based index of the character under that point */
getIndexAtPoint(java.awt.Point point)471     public int getIndexAtPoint(java.awt.Point point) {
472         try {
473             return unoObject.getIndexAtPoint(new Point(point.x, point.y));
474         } catch (com.sun.star.uno.RuntimeException e) {
475             return -1;
476         }
477     }
478 
479     /** Returns the end offset within the selected text */
getSelectionEnd()480     public int getSelectionEnd() {
481         try {
482             int index = unoObject.getSelectionEnd();
483 
484             if (index == -1) {
485                 index = getCaretPosition();
486             }
487 
488             return index;
489         } catch (com.sun.star.uno.RuntimeException e) {
490             return -1;
491         }
492     }
493 
494     /** Returns the string before a given index
495      *
496      *  The Java word iterator has a different understanding of what
497      *  a word is than the word iterator used by OOo, so we use the
498      *  Java iterators to ensure maximal compatibility with Java.
499      */
getBeforeIndex(int part, int index)500     public java.lang.String getBeforeIndex(int part, int index) {
501         switch (part) {
502         case AccessibleText.CHARACTER:
503             try {
504                 String s = unoObject.getText();
505                 return s.substring(index-1, index);
506             } catch (IndexOutOfBoundsException e) {
507                 return null;
508             }
509         case AccessibleText.WORD:
510             try {
511                 String s = unoObject.getText();
512                 BreakIterator words = BreakIterator.getWordInstance(getLocale(index));
513                 words.setText(s);
514                 int end = words.following(index);
515                 end = words.previous();
516                 int start = words.previous();
517                 if (start == BreakIterator.DONE) {
518                     return null;
519                 }
520                 return s.substring(start, end);
521             } catch (IllegalArgumentException e) {
522                 return null;
523             } catch (IndexOutOfBoundsException e) {
524                 return null;
525             }
526         case AccessibleText.SENTENCE:
527             try {
528                 String s = unoObject.getText();
529                 BreakIterator sentence =
530                     BreakIterator.getSentenceInstance(getLocale(index));
531                 sentence.setText(s);
532                 int end = sentence.following(index);
533                 end = sentence.previous();
534                 int start = sentence.previous();
535                 if (start == BreakIterator.DONE) {
536                     return null;
537                 }
538                 return s.substring(start, end);
539             } catch (IllegalArgumentException e) {
540                 return null;
541             } catch (IndexOutOfBoundsException e) {
542                 return null;
543             }
544         case 4:
545             try {
546                 TextSegment ts = unoObject.getTextBeforeIndex(index, AccessibleTextType.LINE);
547                 return ts.SegmentText;
548             } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
549                 // Workaround for #104847#
550                 if (index > 0 && getCharCount() == index) {
551                     return getBeforeIndex(part, index - 1);
552                 }
553                 return null;
554             } catch (com.sun.star.lang.IllegalArgumentException e) {
555                 return null;
556             }
557         case 5:
558             try {
559                 TextSegment ts = unoObject.getTextBeforeIndex(index, AccessibleTextType.ATTRIBUTE_RUN);
560                 return ts.SegmentText;
561             } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
562                 return null;
563             } catch (com.sun.star.lang.IllegalArgumentException e) {
564                 return null;
565             }
566         default:
567             return null;
568         }
569     }
570 
571 
572     /** Returns the string at a given index
573      *
574      *  The Java word iterator has a different understanding of what
575      *  a word is than the word iterator used by OOo, so we use the
576      *  Java iterators to ensure maximal compatibility with Java.
577      */
getAtIndex(int part, int index)578     public java.lang.String getAtIndex(int part, int index) {
579         switch (part) {
580         case AccessibleText.CHARACTER:
581             try {
582                 String s = unoObject.getText();
583                 return s.substring(index, index + 1);
584             } catch (IndexOutOfBoundsException e) {
585                 return null;
586             }
587         case AccessibleText.WORD:
588             try {
589                 String s = unoObject.getText();
590                 BreakIterator words = BreakIterator.getWordInstance(getLocale(index));
591                 words.setText(s);
592                 int end = words.following(index);
593                 return s.substring(words.previous(), end);
594             } catch (IllegalArgumentException e) {
595                 return null;
596             } catch (IndexOutOfBoundsException e) {
597                 return null;
598             }
599         case AccessibleText.SENTENCE:
600             try {
601                 String s = unoObject.getText();
602                 BreakIterator sentence =
603                     BreakIterator.getSentenceInstance(getLocale(index));
604                 sentence.setText(s);
605                 int end = sentence.following(index);
606                 return s.substring(sentence.previous(), end);
607             } catch (IllegalArgumentException e) {
608                 return null;
609             } catch (IndexOutOfBoundsException e) {
610                 return null;
611             }
612         case 4:
613             try {
614                 TextSegment ts = unoObject.getTextAtIndex(index, AccessibleTextType.LINE);
615                 return ts.SegmentText;
616             } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
617                 // Workaround for #104847#
618                 if (index > 0 && getCharCount() == index) {
619                     return getAtIndex(part, index - 1);
620                 }
621                 return null;
622             } catch (com.sun.star.lang.IllegalArgumentException e) {
623                 return null;
624             }
625         case 5:
626             try {
627                 TextSegment ts = unoObject.getTextAtIndex(index, AccessibleTextType.ATTRIBUTE_RUN);
628                 return ts.SegmentText;
629             } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
630                 return null;
631             } catch (com.sun.star.lang.IllegalArgumentException e) {
632                 return null;
633             }
634 
635         default:
636             return null;
637         }
638     }
639 
640     /** Returns the number of characters (valid indicies) */
getCharCount()641     public int getCharCount() {
642         try {
643             return unoObject.getCharacterCount();
644         } catch (com.sun.star.uno.RuntimeException e) {
645         }
646 
647         return 0;
648     }
649 
650     /** Returns the portion of the text that is selected */
getSelectedText()651     public java.lang.String getSelectedText() {
652         try {
653             return unoObject.getSelectedText();
654         } catch (com.sun.star.uno.RuntimeException e) {
655         }
656 
657         return null;
658     }
659 
660     /** Determines the bounding box of the character at the given index into the string */
getCharacterBounds(int index)661     public java.awt.Rectangle getCharacterBounds(int index) {
662         try {
663             Rectangle unoRect = unoObject.getCharacterBounds(index);
664             return new java.awt.Rectangle(unoRect.X, unoRect.Y, unoRect.Width, unoRect.Height);
665         } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
666             if ((index > 0) && (getCharCount() == index)) {
667                 return getCharacterBounds(index - 1);
668             }
669         } catch (com.sun.star.uno.RuntimeException e) {
670         }
671 
672         return new java.awt.Rectangle();
673     }
674 }
675