1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_comphelper.hxx"
30 
31 // includes --------------------------------------------------------------
32 #include <comphelper/accessibletexthelper.hxx>
33 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
34 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
35 #ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_
36 #include <com/sun/star/i18n/WordType.hpp>
37 #endif
38 #include <com/sun/star/i18n/KCharacterType.hpp>
39 #include <comphelper/processfactory.hxx>
40 #include <com/sun/star/accessibility/TextSegment.hpp>
41 
42 #include <algorithm>
43 
44 //..............................................................................
45 namespace comphelper
46 {
47 //..............................................................................
48 
49 	using namespace ::com::sun::star;
50 	using namespace ::com::sun::star::uno;
51 	using namespace ::com::sun::star::lang;
52 	using namespace ::com::sun::star::beans;
53 	using namespace ::com::sun::star::accessibility;
54 
55 	//==============================================================================
56 	// OCommonAccessibleText
57 	//==============================================================================
58 
59 	OCommonAccessibleText::OCommonAccessibleText()
60 	{
61 	}
62 
63 	// -----------------------------------------------------------------------------
64 
65 	OCommonAccessibleText::~OCommonAccessibleText()
66 	{
67 	}
68 
69 	// -----------------------------------------------------------------------------
70 
71 	Reference < i18n::XBreakIterator > OCommonAccessibleText::implGetBreakIterator()
72 	{
73 		if ( !m_xBreakIter.is() )
74 		{
75 			Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
76 			if ( xMSF.is() )
77 			{
78 				m_xBreakIter = Reference< i18n::XBreakIterator >
79 					( xMSF->createInstance( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.BreakIterator" ) ) ), UNO_QUERY );
80 			}
81 		}
82 
83 		return m_xBreakIter;
84 	}
85 
86 	// -----------------------------------------------------------------------------
87 
88 	Reference < i18n::XCharacterClassification > OCommonAccessibleText::implGetCharacterClassification()
89 	{
90 		if ( !m_xCharClass.is() )
91 		{
92 			Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
93 			if ( xMSF.is() )
94 			{
95 				m_xCharClass = Reference< i18n::XCharacterClassification >
96 					( xMSF->createInstance( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.CharacterClassification" ) ) ), UNO_QUERY );
97 			}
98 		}
99 
100 		return m_xCharClass;
101 	}
102 
103 	// -----------------------------------------------------------------------------
104 
105 	sal_Bool OCommonAccessibleText::implIsValidBoundary( i18n::Boundary& rBoundary, sal_Int32 nLength )
106 	{
107 		return ( rBoundary.startPos >= 0 ) && ( rBoundary.startPos < nLength ) && ( rBoundary.endPos >= 0 ) && ( rBoundary.endPos <= nLength );
108 	}
109 
110 	// -----------------------------------------------------------------------------
111 
112 	sal_Bool OCommonAccessibleText::implIsValidIndex( sal_Int32 nIndex, sal_Int32 nLength )
113 	{
114 		return ( nIndex >= 0 ) && ( nIndex < nLength );
115 	}
116 
117 	// -----------------------------------------------------------------------------
118 
119 	sal_Bool OCommonAccessibleText::implIsValidRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex, sal_Int32 nLength )
120 	{
121 		return ( nStartIndex >= 0 ) && ( nStartIndex <= nLength ) && ( nEndIndex >= 0 ) && ( nEndIndex <= nLength );
122 	}
123 
124 	// -----------------------------------------------------------------------------
125 
126 	void OCommonAccessibleText::implGetGlyphBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
127 	{
128 		::rtl::OUString sText( implGetText() );
129 
130 		if ( implIsValidIndex( nIndex, sText.getLength() ) )
131 		{
132 			Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
133 			if ( xBreakIter.is() )
134 			{
135 				sal_Int32 nCount = 1;
136 				sal_Int32 nDone;
137 				sal_Int32 nStartIndex = xBreakIter->previousCharacters( sText, nIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
138 				if ( nDone != 0 )
139 					nStartIndex = xBreakIter->nextCharacters( sText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
140 				sal_Int32 nEndIndex = xBreakIter->nextCharacters( sText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
141 				if ( nDone != 0 )
142 				{
143 					rBoundary.startPos = nStartIndex;
144 					rBoundary.endPos = nEndIndex;
145 				}
146 			}
147 		}
148 		else
149 		{
150 			rBoundary.startPos = nIndex;
151 			rBoundary.endPos = nIndex;
152 		}
153 	}
154 
155 	// -----------------------------------------------------------------------------
156 
157 	sal_Bool OCommonAccessibleText::implGetWordBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
158 	{
159 		sal_Bool bWord = sal_False;
160 		::rtl::OUString sText( implGetText() );
161 
162 		if ( implIsValidIndex( nIndex, sText.getLength() ) )
163 		{
164 			Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
165 			if ( xBreakIter.is() )
166 			{
167 				rBoundary = xBreakIter->getWordBoundary( sText, nIndex, implGetLocale(), i18n::WordType::ANY_WORD, sal_True );
168 
169 				// it's a word, if the first character is an alpha-numeric character
170 				Reference< i18n::XCharacterClassification >	xCharClass = implGetCharacterClassification();
171 				if ( xCharClass.is() )
172 				{
173 					sal_Int32 nType = xCharClass->getCharacterType( sText, rBoundary.startPos, implGetLocale() );
174 					if ( ( nType & ( i18n::KCharacterType::LETTER | i18n::KCharacterType::DIGIT ) ) != 0 )
175 						bWord = sal_True;
176 				}
177 			}
178 		}
179 		else
180 		{
181 			rBoundary.startPos = nIndex;
182 			rBoundary.endPos = nIndex;
183 		}
184 
185 		return bWord;
186 	}
187 
188 	// -----------------------------------------------------------------------------
189 
190 	void OCommonAccessibleText::implGetSentenceBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
191 	{
192 		::rtl::OUString sText( implGetText() );
193 
194 		if ( implIsValidIndex( nIndex, sText.getLength() ) )
195 		{
196 			Locale aLocale = implGetLocale();
197 			Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
198 			if ( xBreakIter.is() )
199 			{
200 				rBoundary.endPos = xBreakIter->endOfSentence( sText, nIndex, aLocale );
201 				rBoundary.startPos = xBreakIter->beginOfSentence( sText, rBoundary.endPos, aLocale );
202 			}
203 		}
204 		else
205 		{
206 			rBoundary.startPos = nIndex;
207 			rBoundary.endPos = nIndex;
208 		}
209 	}
210 
211 	// -----------------------------------------------------------------------------
212 
213 	void OCommonAccessibleText::implGetParagraphBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
214 	{
215 		::rtl::OUString sText( implGetText() );
216 
217 		if ( implIsValidIndex( nIndex, sText.getLength() ) )
218 		{
219 			rBoundary.startPos = 0;
220 			rBoundary.endPos = sText.getLength();
221 
222 			sal_Int32 nFound = sText.lastIndexOf( (sal_Unicode)'\n', nIndex );
223 			if ( nFound != -1 )
224 				rBoundary.startPos = nFound + 1;
225 
226 			nFound = sText.indexOf( (sal_Unicode)'\n', nIndex );
227 			if ( nFound != -1 )
228 				rBoundary.endPos = nFound + 1;
229 		}
230 		else
231 		{
232 			rBoundary.startPos = nIndex;
233 			rBoundary.endPos = nIndex;
234 		}
235 	}
236 
237 	// -----------------------------------------------------------------------------
238 
239 	void OCommonAccessibleText::implGetLineBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
240 	{
241 		::rtl::OUString sText( implGetText() );
242 		sal_Int32 nLength = sText.getLength();
243 
244 		if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
245 		{
246 			rBoundary.startPos = 0;
247 			rBoundary.endPos = nLength;
248 		}
249 		else
250 		{
251 			rBoundary.startPos = nIndex;
252 			rBoundary.endPos = nIndex;
253 		}
254 	}
255 
256 	// -----------------------------------------------------------------------------
257 
258 	sal_Unicode OCommonAccessibleText::getCharacter( sal_Int32 nIndex ) throw (IndexOutOfBoundsException, RuntimeException)
259 	{
260 		::rtl::OUString sText( implGetText() );
261 
262 		if ( !implIsValidIndex( nIndex, sText.getLength() ) )
263 			throw IndexOutOfBoundsException();
264 
265 		return sText.getStr()[nIndex];
266 	}
267 
268 	// -----------------------------------------------------------------------------
269 
270 	sal_Int32 OCommonAccessibleText::getCharacterCount() throw (RuntimeException)
271 	{
272 		return implGetText().getLength();
273 	}
274 
275 	// -----------------------------------------------------------------------------
276 
277 	::rtl::OUString OCommonAccessibleText::getSelectedText() throw (RuntimeException)
278 	{
279 		::rtl::OUString sText;
280 		sal_Int32 nStartIndex;
281 		sal_Int32 nEndIndex;
282 
283 		implGetSelection( nStartIndex, nEndIndex );
284 
285 		try
286 		{
287 			sText = getTextRange( nStartIndex, nEndIndex );
288 		}
289 		catch ( IndexOutOfBoundsException& )
290 		{
291 		}
292 
293 		return sText;
294 	}
295 
296 	// -----------------------------------------------------------------------------
297 
298 	sal_Int32 OCommonAccessibleText::getSelectionStart() throw (RuntimeException)
299 	{
300 		sal_Int32 nStartIndex;
301 		sal_Int32 nEndIndex;
302 
303 		implGetSelection( nStartIndex, nEndIndex );
304 
305 		return nStartIndex;
306 	}
307 
308 	// -----------------------------------------------------------------------------
309 
310 	sal_Int32 OCommonAccessibleText::getSelectionEnd() throw (RuntimeException)
311 	{
312 		sal_Int32 nStartIndex;
313 		sal_Int32 nEndIndex;
314 
315 		implGetSelection( nStartIndex, nEndIndex );
316 
317 		return nEndIndex;
318 	}
319 
320 	// -----------------------------------------------------------------------------
321 
322 	::rtl::OUString OCommonAccessibleText::getText() throw (RuntimeException)
323 	{
324 		return implGetText();
325 	}
326 
327 	// -----------------------------------------------------------------------------
328 
329 	::rtl::OUString OCommonAccessibleText::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (IndexOutOfBoundsException, RuntimeException)
330 	{
331 		::rtl::OUString sText( implGetText() );
332 
333 		if ( !implIsValidRange( nStartIndex, nEndIndex, sText.getLength() ) )
334 			throw IndexOutOfBoundsException();
335 
336 		sal_Int32 nMinIndex = ::std::min( nStartIndex, nEndIndex );
337 		sal_Int32 nMaxIndex = ::std::max( nStartIndex, nEndIndex );
338 
339 		return sText.copy( nMinIndex, nMaxIndex - nMinIndex );
340 	}
341 
342 	// -----------------------------------------------------------------------------
343 
344     TextSegment OCommonAccessibleText::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
345 	{
346 		::rtl::OUString sText( implGetText() );
347 		sal_Int32 nLength = sText.getLength();
348 
349 		if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
350 			throw IndexOutOfBoundsException();
351 
352 		i18n::Boundary aBoundary;
353 		TextSegment aResult;
354 		aResult.SegmentStart = -1;
355 		aResult.SegmentEnd = -1;
356 
357 		switch ( aTextType )
358 		{
359 			case AccessibleTextType::CHARACTER:
360 			{
361 				if ( implIsValidIndex( nIndex, nLength ) )
362 				{
363 					aResult.SegmentText = sText.copy( nIndex, 1 );
364 					aResult.SegmentStart = nIndex;
365 					aResult.SegmentEnd = nIndex+1;
366 				}
367 			}
368 			break;
369 			case AccessibleTextType::GLYPH:
370 			{
371 				// get glyph at index
372 				implGetGlyphBoundary( aBoundary, nIndex );
373 				if ( implIsValidBoundary( aBoundary, nLength ) )
374 			    {
375 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
376 					aResult.SegmentStart = aBoundary.startPos;
377 					aResult.SegmentEnd = aBoundary.endPos;
378 				}
379 			}
380 			break;
381 			case AccessibleTextType::WORD:
382 			{
383 				// get word at index
384 				sal_Bool bWord = implGetWordBoundary( aBoundary, nIndex );
385 				if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
386 				{
387 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
388 					aResult.SegmentStart = aBoundary.startPos;
389 					aResult.SegmentEnd = aBoundary.endPos;
390 				}
391 			}
392 			break;
393 			case AccessibleTextType::SENTENCE:
394 			{
395 				// get sentence at index
396 				implGetSentenceBoundary( aBoundary, nIndex );
397 				if ( implIsValidBoundary( aBoundary, nLength ) )
398 				{
399 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
400 					aResult.SegmentStart = aBoundary.startPos;
401 					aResult.SegmentEnd = aBoundary.endPos;
402 				}
403 			}
404 			break;
405 			case AccessibleTextType::PARAGRAPH:
406 			{
407 				// get paragraph at index
408 				implGetParagraphBoundary( aBoundary, nIndex );
409 				if ( implIsValidBoundary( aBoundary, nLength ) )
410 				{
411 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
412 					aResult.SegmentStart = aBoundary.startPos;
413 					aResult.SegmentEnd = aBoundary.endPos;
414 				}
415 			}
416 			break;
417 			case AccessibleTextType::LINE:
418 			{
419 				// get line at index
420 				implGetLineBoundary( aBoundary, nIndex );
421 				if ( implIsValidBoundary( aBoundary, nLength ) )
422 				{
423 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
424 					aResult.SegmentStart = aBoundary.startPos;
425 					aResult.SegmentEnd = aBoundary.endPos;
426 				}
427 			}
428 			break;
429             case AccessibleTextType::ATTRIBUTE_RUN:
430             {
431                 // TODO: implGetAttributeRunBoundary() (incompatible!)
432 
433                 aResult.SegmentText = sText;
434                 aResult.SegmentStart = 0;
435                 aResult.SegmentEnd = nLength;
436             }
437             break;
438 			default:
439 			{
440 				// unknown text type
441 			}
442 		}
443 
444 		return aResult;
445 	}
446 
447 	// -----------------------------------------------------------------------------
448 
449     TextSegment OCommonAccessibleText::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
450 	{
451 		::rtl::OUString sText( implGetText() );
452 		sal_Int32 nLength = sText.getLength();
453 
454 		if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
455 			throw IndexOutOfBoundsException();
456 
457 		i18n::Boundary aBoundary;
458 		TextSegment aResult;
459 		aResult.SegmentStart = -1;
460 		aResult.SegmentEnd = -1;
461 
462 		switch ( aTextType )
463 		{
464 			case AccessibleTextType::CHARACTER:
465 			{
466 				if ( implIsValidIndex( nIndex - 1, nLength ) )
467 				{
468 					aResult.SegmentText = sText.copy( nIndex - 1, 1 );
469 					aResult.SegmentStart = nIndex-1;
470 					aResult.SegmentEnd = nIndex;
471 				}
472 			}
473 			break;
474 			case AccessibleTextType::GLYPH:
475 			{
476 				// get glyph at index
477 				implGetGlyphBoundary( aBoundary, nIndex );
478 				// get previous glyph
479 				if ( aBoundary.startPos > 0 )
480 				{
481 					implGetGlyphBoundary( aBoundary, aBoundary.startPos - 1 );
482 					if ( implIsValidBoundary( aBoundary, nLength ) )
483 					{
484 						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
485     					aResult.SegmentStart = aBoundary.startPos;
486     					aResult.SegmentEnd = aBoundary.endPos;
487 					}
488 				}
489 			}
490 			break;
491 			case AccessibleTextType::WORD:
492 			{
493 				// get word at index
494 				implGetWordBoundary( aBoundary, nIndex );
495 				// get previous word
496 				sal_Bool bWord = sal_False;
497 				while ( !bWord && aBoundary.startPos > 0 )
498 					bWord = implGetWordBoundary( aBoundary, aBoundary.startPos - 1 );
499 				if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
500 				{
501 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
502 					aResult.SegmentStart = aBoundary.startPos;
503 					aResult.SegmentEnd = aBoundary.endPos;
504 				}
505 			}
506 			break;
507 			case AccessibleTextType::SENTENCE:
508 			{
509 				// get sentence at index
510 				implGetSentenceBoundary( aBoundary, nIndex );
511 				// get previous sentence
512 				if ( aBoundary.startPos > 0 )
513 				{
514 					implGetSentenceBoundary( aBoundary, aBoundary.startPos - 1 );
515 					if ( implIsValidBoundary( aBoundary, nLength ) )
516 					{
517 						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
518     					aResult.SegmentStart = aBoundary.startPos;
519     					aResult.SegmentEnd = aBoundary.endPos;
520 					}
521 				}
522 			}
523 			break;
524 			case AccessibleTextType::PARAGRAPH:
525 			{
526 				// get paragraph at index
527 				implGetParagraphBoundary( aBoundary, nIndex );
528 				// get previous paragraph
529 				if ( aBoundary.startPos > 0 )
530 				{
531 					implGetParagraphBoundary( aBoundary, aBoundary.startPos - 1 );
532 					if ( implIsValidBoundary( aBoundary, nLength ) )
533 					{
534 						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
535     					aResult.SegmentStart = aBoundary.startPos;
536     					aResult.SegmentEnd = aBoundary.endPos;
537 					}
538 				}
539 			}
540 			break;
541 			case AccessibleTextType::LINE:
542 			{
543 				// get line at index
544 				implGetLineBoundary( aBoundary, nIndex );
545 				// get previous line
546 				if ( aBoundary.startPos > 0 )
547 				{
548 					implGetLineBoundary( aBoundary, aBoundary.startPos - 1 );
549 					if ( implIsValidBoundary( aBoundary, nLength ) )
550 					{
551 						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
552     					aResult.SegmentStart = aBoundary.startPos;
553     					aResult.SegmentEnd = aBoundary.endPos;
554 					}
555 				}
556 			}
557 			break;
558             case AccessibleTextType::ATTRIBUTE_RUN:
559             {
560                 // TODO: implGetAttributeRunBoundary() (incompatible!)
561             }
562             break;
563 			default:
564 			{
565 				// unknown text type
566 			}
567 		}
568 
569 		return aResult;
570 	}
571 
572 	// -----------------------------------------------------------------------------
573 
574     TextSegment OCommonAccessibleText::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
575 	{
576 		::rtl::OUString sText( implGetText() );
577 		sal_Int32 nLength = sText.getLength();
578 
579 		if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
580 			throw IndexOutOfBoundsException();
581 
582 		i18n::Boundary aBoundary;
583 		TextSegment aResult;
584 		aResult.SegmentStart = -1;
585 		aResult.SegmentEnd = -1;
586 
587 		switch ( aTextType )
588 		{
589 			case AccessibleTextType::CHARACTER:
590 			{
591 				if ( implIsValidIndex( nIndex + 1, nLength ) )
592 			    {
593 					aResult.SegmentText = sText.copy( nIndex + 1, 1 );
594 					aResult.SegmentStart = nIndex+1;
595 					aResult.SegmentEnd = nIndex+2;
596 				}
597 			}
598 			break;
599 			case AccessibleTextType::GLYPH:
600 			{
601 				// get glyph at index
602 				implGetGlyphBoundary( aBoundary, nIndex );
603 				// get next glyph
604 				if ( aBoundary.endPos < nLength )
605 				{
606 					implGetGlyphBoundary( aBoundary, aBoundary.endPos );
607 					if ( implIsValidBoundary( aBoundary, nLength ) )
608 					{
609 						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
610     					aResult.SegmentStart = aBoundary.startPos;
611     					aResult.SegmentEnd = aBoundary.endPos;
612 					}
613 				}
614 			}
615 			break;
616 			case AccessibleTextType::WORD:
617 			{
618 				// get word at index
619 				implGetWordBoundary( aBoundary, nIndex );
620 				// get next word
621 				sal_Bool bWord = sal_False;
622 				while ( !bWord && aBoundary.endPos < nLength )
623 					bWord = implGetWordBoundary( aBoundary, aBoundary.endPos );
624 				if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
625 				{
626 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
627 					aResult.SegmentStart = aBoundary.startPos;
628 					aResult.SegmentEnd = aBoundary.endPos;
629 				}
630 			}
631 			break;
632 			case AccessibleTextType::SENTENCE:
633 			{
634 				// get sentence at index
635 				implGetSentenceBoundary( aBoundary, nIndex );
636 				// get next sentence
637 				sal_Int32 nEnd = aBoundary.endPos;
638 				sal_Int32 nI = aBoundary.endPos;
639 				sal_Bool bFound = sal_False;
640 				while ( !bFound && ++nI < nLength )
641 				{
642 					implGetSentenceBoundary( aBoundary, nI );
643 					bFound = ( aBoundary.endPos > nEnd );
644 				}
645 				if ( bFound && implIsValidBoundary( aBoundary, nLength ) )
646 				{
647 					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
648 					aResult.SegmentStart = aBoundary.startPos;
649 					aResult.SegmentEnd = aBoundary.endPos;
650 				}
651 			}
652 			break;
653 			case AccessibleTextType::PARAGRAPH:
654 			{
655 				// get paragraph at index
656 				implGetParagraphBoundary( aBoundary, nIndex );
657 				// get next paragraph
658 				if ( aBoundary.endPos < nLength )
659 				{
660 					implGetParagraphBoundary( aBoundary, aBoundary.endPos );
661 					if ( implIsValidBoundary( aBoundary, nLength ) )
662 					{
663 						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
664     					aResult.SegmentStart = aBoundary.startPos;
665     					aResult.SegmentEnd = aBoundary.endPos;
666 					}
667 				}
668 			}
669 			break;
670 			case AccessibleTextType::LINE:
671 			{
672 				// get line at index
673 				implGetLineBoundary( aBoundary, nIndex );
674 				// get next line
675 				if ( aBoundary.endPos < nLength )
676 				{
677 					implGetLineBoundary( aBoundary, aBoundary.endPos );
678 					if ( implIsValidBoundary( aBoundary, nLength ) )
679 					{
680 						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
681     					aResult.SegmentStart = aBoundary.startPos;
682     					aResult.SegmentEnd = aBoundary.endPos;
683 					}
684 				}
685 			}
686 			break;
687             case AccessibleTextType::ATTRIBUTE_RUN:
688             {
689                 // TODO: implGetAttributeRunBoundary() (incompatible!)
690             }
691             break;
692 			default:
693 			{
694 				// unknown text type
695 			}
696 		}
697 
698 		return aResult;
699 	}
700 
701     // -----------------------------------------------------------------------------
702     bool OCommonAccessibleText::implInitTextChangedEvent(
703         const rtl::OUString& rOldString,
704         const rtl::OUString& rNewString,
705         ::com::sun::star::uno::Any& rDeleted,
706         ::com::sun::star::uno::Any& rInserted) // throw()
707     {
708         sal_uInt32 nLenOld = rOldString.getLength();
709         sal_uInt32 nLenNew = rNewString.getLength();
710 
711         // equal
712         if ((0 == nLenOld) && (0 == nLenNew))
713             return false;
714 
715         TextSegment aDeletedText;
716         TextSegment aInsertedText;
717 
718         aDeletedText.SegmentStart = -1;
719         aDeletedText.SegmentEnd = -1;
720         aInsertedText.SegmentStart = -1;
721         aInsertedText.SegmentEnd = -1;
722 
723         // insert only
724         if ((0 == nLenOld) && (nLenNew > 0))
725         {
726             aInsertedText.SegmentStart = 0;
727             aInsertedText.SegmentEnd = nLenNew;
728             aInsertedText.SegmentText = rNewString.copy( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );
729 
730             rInserted <<= aInsertedText;
731             return true;
732         }
733 
734         // delete only
735         if ((nLenOld > 0) && (0 == nLenNew))
736         {
737             aDeletedText.SegmentStart = 0;
738             aDeletedText.SegmentEnd = nLenOld;
739             aDeletedText.SegmentText = rOldString.copy( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );
740 
741             rDeleted <<= aDeletedText;
742             return true;
743         }
744 
745         const sal_Unicode* pFirstDiffOld = rOldString.getStr();
746         const sal_Unicode* pLastDiffOld  = rOldString.getStr() + nLenOld;
747         const sal_Unicode* pFirstDiffNew = rNewString.getStr();
748         const sal_Unicode* pLastDiffNew  = rNewString.getStr() + nLenNew;
749 
750         // find first difference
751         while ((*pFirstDiffOld == *pFirstDiffNew) &&
752                (pFirstDiffOld  <  pLastDiffOld) &&
753                (pFirstDiffNew  <  pLastDiffNew))
754         {
755             pFirstDiffOld++;
756             pFirstDiffNew++;
757         }
758 
759         // equality test
760         if ((0 == *pFirstDiffOld) && (0 == *pFirstDiffNew))
761             return false;
762 
763         // find last difference
764         while ( ( pLastDiffOld > pFirstDiffOld) &&
765                 ( pLastDiffNew > pFirstDiffNew) &&
766                 (pLastDiffOld[-1]  == pLastDiffNew[-1]))
767         {
768             pLastDiffOld--;
769             pLastDiffNew--;
770         }
771 
772         if (pFirstDiffOld < pLastDiffOld)
773         {
774             aDeletedText.SegmentStart = pFirstDiffOld - rOldString.getStr();
775             aDeletedText.SegmentEnd = pLastDiffOld  - rOldString.getStr();
776             aDeletedText.SegmentText = rOldString.copy( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );
777 
778             rDeleted <<= aDeletedText;
779         }
780 
781         if (pFirstDiffNew < pLastDiffNew)
782         {
783             aInsertedText.SegmentStart = pFirstDiffNew - rNewString.getStr();
784             aInsertedText.SegmentEnd = pLastDiffNew  - rNewString.getStr();
785             aInsertedText.SegmentText = rNewString.copy( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );
786 
787             rInserted <<= aInsertedText;
788         }
789         return true;
790     }
791 
792 	//==============================================================================
793 	// OAccessibleTextHelper
794 	//==============================================================================
795 
796 	OAccessibleTextHelper::OAccessibleTextHelper()
797 	{
798 	}
799 
800 	// -----------------------------------------------------------------------------
801 
802 	OAccessibleTextHelper::OAccessibleTextHelper( IMutex* _pExternalLock )
803 		:OAccessibleExtendedComponentHelper( _pExternalLock )
804 	{
805 	}
806 
807 	// -----------------------------------------------------------------------------
808 	// XInterface
809 	// -----------------------------------------------------------------------------
810 
811 	IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )
812 
813 	// -----------------------------------------------------------------------------
814 	// XTypeProvider
815 	// -----------------------------------------------------------------------------
816 
817 	IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )
818 
819 	// -----------------------------------------------------------------------------
820 	// XAccessibleText
821 	// -----------------------------------------------------------------------------
822 
823 	sal_Unicode OAccessibleTextHelper::getCharacter( sal_Int32 nIndex ) throw (IndexOutOfBoundsException, RuntimeException)
824 	{
825 		OExternalLockGuard aGuard( this );
826 
827 		return OCommonAccessibleText::getCharacter( nIndex );
828 	}
829 
830 	// -----------------------------------------------------------------------------
831 
832 	sal_Int32 OAccessibleTextHelper::getCharacterCount() throw (RuntimeException)
833 	{
834 		OExternalLockGuard aGuard( this );
835 
836 		return OCommonAccessibleText::getCharacterCount();
837 	}
838 
839 	// -----------------------------------------------------------------------------
840 
841 	::rtl::OUString OAccessibleTextHelper::getSelectedText() throw (RuntimeException)
842 	{
843 		OExternalLockGuard aGuard( this );
844 
845 		return OCommonAccessibleText::getSelectedText();
846 	}
847 
848 	// -----------------------------------------------------------------------------
849 
850 	sal_Int32 OAccessibleTextHelper::getSelectionStart() throw (RuntimeException)
851 	{
852 		OExternalLockGuard aGuard( this );
853 
854 		return OCommonAccessibleText::getSelectionStart();
855 	}
856 
857 	// -----------------------------------------------------------------------------
858 
859 	sal_Int32 OAccessibleTextHelper::getSelectionEnd() throw (RuntimeException)
860 	{
861 		OExternalLockGuard aGuard( this );
862 
863 		return OCommonAccessibleText::getSelectionEnd();
864 	}
865 
866 	// -----------------------------------------------------------------------------
867 
868 	::rtl::OUString OAccessibleTextHelper::getText() throw (RuntimeException)
869 	{
870 		OExternalLockGuard aGuard( this );
871 
872 		return OCommonAccessibleText::getText();
873 	}
874 
875 	// -----------------------------------------------------------------------------
876 
877 	::rtl::OUString OAccessibleTextHelper::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (IndexOutOfBoundsException, RuntimeException)
878 	{
879 		OExternalLockGuard aGuard( this );
880 
881 		return OCommonAccessibleText::getTextRange( nStartIndex, nEndIndex );
882 	}
883 
884 	// -----------------------------------------------------------------------------
885 
886     TextSegment OAccessibleTextHelper::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
887 	{
888 		OExternalLockGuard aGuard( this );
889 
890 		return OCommonAccessibleText::getTextAtIndex( nIndex, aTextType );
891 	}
892 
893 	// -----------------------------------------------------------------------------
894 
895     TextSegment OAccessibleTextHelper::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
896 	{
897 		OExternalLockGuard aGuard( this );
898 
899 		return OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType );
900 	}
901 
902 	// -----------------------------------------------------------------------------
903 
904     TextSegment OAccessibleTextHelper::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
905 	{
906 		OExternalLockGuard aGuard( this );
907 
908 		return OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType );
909 	}
910 
911 	// -----------------------------------------------------------------------------
912 
913 //..............................................................................
914 }	// namespace comphelper
915 //..............................................................................
916