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