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