xref: /trunk/main/comphelper/source/misc/accessibletexthelper.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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