1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <comphelper/accessibletexthelper.hxx>
21 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
22 #include <com/sun/star/i18n/BreakIterator.hpp>
23 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
24 #include <com/sun/star/i18n/CharacterClassification.hpp>
25 #include <com/sun/star/i18n/WordType.hpp>
26 #include <com/sun/star/i18n/KCharacterType.hpp>
27 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
28 #include <comphelper/processfactory.hxx>
29 #include <com/sun/star/accessibility/TextSegment.hpp>
30 
31 #include <algorithm>
32 
33 
34 namespace comphelper
35 {
36 
37 
38     using namespace ::com::sun::star;
39     using namespace ::com::sun::star::uno;
40     using namespace ::com::sun::star::lang;
41     using namespace ::com::sun::star::beans;
42     using namespace ::com::sun::star::accessibility;
43 
44 
45     // OCommonAccessibleText
46 
47 
OCommonAccessibleText()48     OCommonAccessibleText::OCommonAccessibleText()
49     {
50     }
51 
52 
~OCommonAccessibleText()53     OCommonAccessibleText::~OCommonAccessibleText()
54     {
55     }
56 
57 
implGetBreakIterator()58     Reference < i18n::XBreakIterator > const & OCommonAccessibleText::implGetBreakIterator()
59     {
60         if ( !m_xBreakIter.is() )
61         {
62             Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
63             m_xBreakIter = i18n::BreakIterator::create(xContext);
64         }
65 
66         return m_xBreakIter;
67     }
68 
69 
implGetCharacterClassification()70     Reference < i18n::XCharacterClassification > const & OCommonAccessibleText::implGetCharacterClassification()
71     {
72         if ( !m_xCharClass.is() )
73         {
74             m_xCharClass = i18n::CharacterClassification::create( ::comphelper::getProcessComponentContext() );
75         }
76 
77         return m_xCharClass;
78     }
79 
80 
implIsValidBoundary(i18n::Boundary const & rBoundary,sal_Int32 nLength)81     bool OCommonAccessibleText::implIsValidBoundary( i18n::Boundary const & rBoundary, sal_Int32 nLength )
82     {
83         return ( rBoundary.startPos >= 0 ) && ( rBoundary.startPos < nLength ) && ( rBoundary.endPos >= 0 ) && ( rBoundary.endPos <= nLength );
84     }
85 
86 
implIsValidIndex(sal_Int32 nIndex,sal_Int32 nLength)87     bool OCommonAccessibleText::implIsValidIndex( sal_Int32 nIndex, sal_Int32 nLength )
88     {
89         return ( nIndex >= 0 ) && ( nIndex < nLength );
90     }
91 
92 
implIsValidRange(sal_Int32 nStartIndex,sal_Int32 nEndIndex,sal_Int32 nLength)93     bool OCommonAccessibleText::implIsValidRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex, sal_Int32 nLength )
94     {
95         return ( nStartIndex >= 0 ) && ( nStartIndex <= nLength ) && ( nEndIndex >= 0 ) && ( nEndIndex <= nLength );
96     }
97 
98 
implGetGlyphBoundary(const OUString & rText,i18n::Boundary & rBoundary,sal_Int32 nIndex)99     void OCommonAccessibleText::implGetGlyphBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
100     {
101         if ( implIsValidIndex( nIndex, rText.getLength() ) )
102         {
103             Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
104             if ( xBreakIter.is() )
105             {
106                 sal_Int32 nCount = 1;
107                 sal_Int32 nDone;
108                 sal_Int32 nStartIndex = xBreakIter->previousCharacters( rText, nIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
109                 if ( nDone != 0 )
110                     nStartIndex = xBreakIter->nextCharacters( rText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
111                 sal_Int32 nEndIndex = xBreakIter->nextCharacters( rText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
112                 if ( nDone != 0 )
113                 {
114                     rBoundary.startPos = nStartIndex;
115                     rBoundary.endPos = nEndIndex;
116                 }
117             }
118         }
119         else
120         {
121             rBoundary.startPos = nIndex;
122             rBoundary.endPos = nIndex;
123         }
124     }
125 
126 
implGetWordBoundary(const OUString & rText,i18n::Boundary & rBoundary,sal_Int32 nIndex)127     bool OCommonAccessibleText::implGetWordBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
128     {
129         bool bWord = false;
130 
131         if ( implIsValidIndex( nIndex, rText.getLength() ) )
132         {
133             Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
134             if ( xBreakIter.is() )
135             {
136                 rBoundary = xBreakIter->getWordBoundary( rText, nIndex, implGetLocale(), i18n::WordType::ANY_WORD, true );
137 
138                 // it's a word, if the first character is an alpha-numeric character
139                 Reference< i18n::XCharacterClassification > xCharClass = implGetCharacterClassification();
140                 if ( xCharClass.is() )
141                 {
142                     sal_Int32 nType = xCharClass->getCharacterType( rText, rBoundary.startPos, implGetLocale() );
143                     if ( ( nType & ( i18n::KCharacterType::LETTER | i18n::KCharacterType::DIGIT ) ) != 0 )
144                         bWord = true;
145                 }
146             }
147         }
148         else
149         {
150             rBoundary.startPos = nIndex;
151             rBoundary.endPos = nIndex;
152         }
153 
154         return bWord;
155     }
156 
157 
implGetSentenceBoundary(const OUString & rText,i18n::Boundary & rBoundary,sal_Int32 nIndex)158     void OCommonAccessibleText::implGetSentenceBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
159     {
160         if ( implIsValidIndex( nIndex, rText.getLength() ) )
161         {
162             Locale aLocale = implGetLocale();
163             Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
164             if ( xBreakIter.is() )
165             {
166                 rBoundary.endPos = xBreakIter->endOfSentence( rText, nIndex, aLocale );
167                 rBoundary.startPos = xBreakIter->beginOfSentence( rText, rBoundary.endPos, aLocale );
168             }
169         }
170         else
171         {
172             rBoundary.startPos = nIndex;
173             rBoundary.endPos = nIndex;
174         }
175     }
176 
177 
implGetParagraphBoundary(const OUString & rText,i18n::Boundary & rBoundary,sal_Int32 nIndex)178     void OCommonAccessibleText::implGetParagraphBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
179     {
180         if ( implIsValidIndex( nIndex, rText.getLength() ) )
181         {
182             rBoundary.startPos = 0;
183             rBoundary.endPos = rText.getLength();
184 
185             sal_Int32 nFound = rText.lastIndexOf( '\n', nIndex );
186             if ( nFound != -1 )
187                 rBoundary.startPos = nFound + 1;
188 
189             nFound = rText.indexOf( '\n', nIndex );
190             if ( nFound != -1 )
191                 rBoundary.endPos = nFound + 1;
192         }
193         else
194         {
195             rBoundary.startPos = nIndex;
196             rBoundary.endPos = nIndex;
197         }
198     }
199 
200 
implGetLineBoundary(const OUString & rText,i18n::Boundary & rBoundary,sal_Int32 nIndex)201     void OCommonAccessibleText::implGetLineBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
202     {
203         sal_Int32 nLength = rText.getLength();
204 
205         if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
206         {
207             rBoundary.startPos = 0;
208             rBoundary.endPos = nLength;
209         }
210         else
211         {
212             rBoundary.startPos = nIndex;
213             rBoundary.endPos = nIndex;
214         }
215     }
216 
217 
implGetCharacter(const OUString & rText,sal_Int32 nIndex)218     sal_Unicode OCommonAccessibleText::implGetCharacter( const OUString& rText, sal_Int32 nIndex )
219     {
220         if ( !implIsValidIndex( nIndex, rText.getLength() ) )
221             throw IndexOutOfBoundsException();
222 
223         return rText[nIndex];
224     }
225 
getSelectedText()226     OUString OCommonAccessibleText::getSelectedText()
227     {
228         OUString sText;
229         sal_Int32 nStartIndex;
230         sal_Int32 nEndIndex;
231 
232         implGetSelection( nStartIndex, nEndIndex );
233 
234         try
235         {
236             sText = implGetTextRange( implGetText(), nStartIndex, nEndIndex );
237         }
238         catch ( IndexOutOfBoundsException& )
239         {
240         }
241 
242         return sText;
243     }
244 
245 
getSelectionStart()246     sal_Int32 OCommonAccessibleText::getSelectionStart()
247     {
248         sal_Int32 nStartIndex;
249         sal_Int32 nEndIndex;
250 
251         implGetSelection( nStartIndex, nEndIndex );
252 
253         return nStartIndex;
254     }
255 
256 
getSelectionEnd()257     sal_Int32 OCommonAccessibleText::getSelectionEnd()
258     {
259         sal_Int32 nStartIndex;
260         sal_Int32 nEndIndex;
261 
262         implGetSelection( nStartIndex, nEndIndex );
263 
264         return nEndIndex;
265     }
266 
267 
implGetTextRange(const OUString & rText,sal_Int32 nStartIndex,sal_Int32 nEndIndex)268     OUString OCommonAccessibleText::implGetTextRange( const OUString& rText, sal_Int32 nStartIndex, sal_Int32 nEndIndex )
269     {
270 
271         if ( !implIsValidRange( nStartIndex, nEndIndex, rText.getLength() ) )
272             throw IndexOutOfBoundsException();
273 
274         sal_Int32 nMinIndex = std::min( nStartIndex, nEndIndex );
275         sal_Int32 nMaxIndex = std::max( nStartIndex, nEndIndex );
276 
277         return rText.copy( nMinIndex, nMaxIndex - nMinIndex );
278     }
279 
getTextAtIndex(sal_Int32 nIndex,sal_Int16 aTextType)280     TextSegment OCommonAccessibleText::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
281     {
282         OUString sText( implGetText() );
283         sal_Int32 nLength = sText.getLength();
284 
285         if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
286             throw IndexOutOfBoundsException();
287 
288         i18n::Boundary aBoundary;
289         TextSegment aResult;
290         aResult.SegmentStart = -1;
291         aResult.SegmentEnd = -1;
292 
293         switch ( aTextType )
294         {
295             case AccessibleTextType::CHARACTER:
296             {
297                 if ( implIsValidIndex( nIndex, nLength ) )
298                 {
299                     aResult.SegmentText = sText.copy( nIndex, 1 );
300                     aResult.SegmentStart = nIndex;
301                     aResult.SegmentEnd = nIndex+1;
302                 }
303             }
304             break;
305             case AccessibleTextType::GLYPH:
306             {
307                 // get glyph at index
308                 implGetGlyphBoundary( sText, aBoundary, nIndex );
309                 if ( implIsValidBoundary( aBoundary, nLength ) )
310                 {
311                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
312                     aResult.SegmentStart = aBoundary.startPos;
313                     aResult.SegmentEnd = aBoundary.endPos;
314                 }
315             }
316             break;
317             case AccessibleTextType::WORD:
318             {
319                 // get word at index
320                 bool bWord = implGetWordBoundary( sText, aBoundary, nIndex );
321                 if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
322                 {
323                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
324                     aResult.SegmentStart = aBoundary.startPos;
325                     aResult.SegmentEnd = aBoundary.endPos;
326                 }
327             }
328             break;
329             case AccessibleTextType::SENTENCE:
330             {
331                 // get sentence at index
332                 implGetSentenceBoundary( sText, aBoundary, nIndex );
333                 if ( implIsValidBoundary( aBoundary, nLength ) )
334                 {
335                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
336                     aResult.SegmentStart = aBoundary.startPos;
337                     aResult.SegmentEnd = aBoundary.endPos;
338                 }
339             }
340             break;
341             case AccessibleTextType::PARAGRAPH:
342             {
343                 // get paragraph at index
344                 implGetParagraphBoundary( sText, aBoundary, nIndex );
345                 if ( implIsValidBoundary( aBoundary, nLength ) )
346                 {
347                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
348                     aResult.SegmentStart = aBoundary.startPos;
349                     aResult.SegmentEnd = aBoundary.endPos;
350                 }
351             }
352             break;
353             case AccessibleTextType::LINE:
354             {
355                 // get line at index
356                 implGetLineBoundary( sText, aBoundary, nIndex );
357                 if ( implIsValidBoundary( aBoundary, nLength ) )
358                 {
359                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
360                     aResult.SegmentStart = aBoundary.startPos;
361                     aResult.SegmentEnd = aBoundary.endPos;
362                 }
363             }
364             break;
365             case AccessibleTextType::ATTRIBUTE_RUN:
366             {
367                 // TODO: implGetAttributeRunBoundary() (incompatible!)
368 
369                 aResult.SegmentText = sText;
370                 aResult.SegmentStart = 0;
371                 aResult.SegmentEnd = nLength;
372             }
373             break;
374             default:
375             {
376                 // unknown text type
377             }
378         }
379 
380         return aResult;
381     }
382 
383 
getTextBeforeIndex(sal_Int32 nIndex,sal_Int16 aTextType)384     TextSegment OCommonAccessibleText::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
385     {
386         OUString sText( implGetText() );
387         sal_Int32 nLength = sText.getLength();
388 
389         if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
390             throw IndexOutOfBoundsException();
391 
392         i18n::Boundary aBoundary;
393         TextSegment aResult;
394         aResult.SegmentStart = -1;
395         aResult.SegmentEnd = -1;
396 
397         switch ( aTextType )
398         {
399             case AccessibleTextType::CHARACTER:
400             {
401                 if ( implIsValidIndex( nIndex - 1, nLength ) )
402                 {
403                     aResult.SegmentText = sText.copy( nIndex - 1, 1 );
404                     aResult.SegmentStart = nIndex-1;
405                     aResult.SegmentEnd = nIndex;
406                 }
407             }
408             break;
409             case AccessibleTextType::GLYPH:
410             {
411                 // get glyph at index
412                 implGetGlyphBoundary( sText, aBoundary, nIndex );
413                 // get previous glyph
414                 if ( aBoundary.startPos > 0 )
415                 {
416                     implGetGlyphBoundary( sText, aBoundary, aBoundary.startPos - 1 );
417                     if ( implIsValidBoundary( aBoundary, nLength ) )
418                     {
419                         aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
420                         aResult.SegmentStart = aBoundary.startPos;
421                         aResult.SegmentEnd = aBoundary.endPos;
422                     }
423                 }
424             }
425             break;
426             case AccessibleTextType::WORD:
427             {
428                 // get word at index
429                 implGetWordBoundary( sText, aBoundary, nIndex );
430                 // get previous word
431                 bool bWord = false;
432                 while ( !bWord && aBoundary.startPos > 0 )
433                     bWord = implGetWordBoundary( sText, aBoundary, aBoundary.startPos - 1 );
434                 if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
435                 {
436                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
437                     aResult.SegmentStart = aBoundary.startPos;
438                     aResult.SegmentEnd = aBoundary.endPos;
439                 }
440             }
441             break;
442             case AccessibleTextType::SENTENCE:
443             {
444                 // get sentence at index
445                 implGetSentenceBoundary( sText, aBoundary, nIndex );
446                 // get previous sentence
447                 if ( aBoundary.startPos > 0 )
448                 {
449                     implGetSentenceBoundary( sText, aBoundary, aBoundary.startPos - 1 );
450                     if ( implIsValidBoundary( aBoundary, nLength ) )
451                     {
452                         aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
453                         aResult.SegmentStart = aBoundary.startPos;
454                         aResult.SegmentEnd = aBoundary.endPos;
455                     }
456                 }
457             }
458             break;
459             case AccessibleTextType::PARAGRAPH:
460             {
461                 // get paragraph at index
462                 implGetParagraphBoundary( sText, aBoundary, nIndex );
463                 // get previous paragraph
464                 if ( aBoundary.startPos > 0 )
465                 {
466                     implGetParagraphBoundary( sText, aBoundary, aBoundary.startPos - 1 );
467                     if ( implIsValidBoundary( aBoundary, nLength ) )
468                     {
469                         aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
470                         aResult.SegmentStart = aBoundary.startPos;
471                         aResult.SegmentEnd = aBoundary.endPos;
472                     }
473                 }
474             }
475             break;
476             case AccessibleTextType::LINE:
477             {
478                 // get line at index
479                 implGetLineBoundary( sText, aBoundary, nIndex );
480                 // get previous line
481                 if ( aBoundary.startPos > 0 )
482                 {
483                     implGetLineBoundary( sText, aBoundary, aBoundary.startPos - 1 );
484                     if ( implIsValidBoundary( aBoundary, nLength ) )
485                     {
486                         aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
487                         aResult.SegmentStart = aBoundary.startPos;
488                         aResult.SegmentEnd = aBoundary.endPos;
489                     }
490                 }
491             }
492             break;
493             case AccessibleTextType::ATTRIBUTE_RUN:
494             {
495                 // TODO: implGetAttributeRunBoundary() (incompatible!)
496             }
497             break;
498             default:
499             {
500                 // unknown text type
501             }
502         }
503 
504         return aResult;
505     }
506 
507 
getTextBehindIndex(sal_Int32 nIndex,sal_Int16 aTextType)508     TextSegment OCommonAccessibleText::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
509     {
510         OUString sText( implGetText() );
511         sal_Int32 nLength = sText.getLength();
512 
513         if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
514             throw IndexOutOfBoundsException();
515 
516         i18n::Boundary aBoundary;
517         TextSegment aResult;
518         aResult.SegmentStart = -1;
519         aResult.SegmentEnd = -1;
520 
521         switch ( aTextType )
522         {
523             case AccessibleTextType::CHARACTER:
524             {
525                 if ( implIsValidIndex( nIndex + 1, nLength ) )
526                 {
527                     aResult.SegmentText = sText.copy( nIndex + 1, 1 );
528                     aResult.SegmentStart = nIndex+1;
529                     aResult.SegmentEnd = nIndex+2;
530                 }
531             }
532             break;
533             case AccessibleTextType::GLYPH:
534             {
535                 // get glyph at index
536                 implGetGlyphBoundary( sText, aBoundary, nIndex );
537                 // get next glyph
538                 if ( aBoundary.endPos < nLength )
539                 {
540                     implGetGlyphBoundary( sText, aBoundary, aBoundary.endPos );
541                     if ( implIsValidBoundary( aBoundary, nLength ) )
542                     {
543                         aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
544                         aResult.SegmentStart = aBoundary.startPos;
545                         aResult.SegmentEnd = aBoundary.endPos;
546                     }
547                 }
548             }
549             break;
550             case AccessibleTextType::WORD:
551             {
552                 // get word at index
553                 implGetWordBoundary( sText, aBoundary, nIndex );
554                 // get next word
555                 bool bWord = false;
556                 while ( !bWord && aBoundary.endPos < nLength )
557                     bWord = implGetWordBoundary( sText, aBoundary, aBoundary.endPos );
558                 if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
559                 {
560                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
561                     aResult.SegmentStart = aBoundary.startPos;
562                     aResult.SegmentEnd = aBoundary.endPos;
563                 }
564             }
565             break;
566             case AccessibleTextType::SENTENCE:
567             {
568                 // get sentence at index
569                 implGetSentenceBoundary( sText, aBoundary, nIndex );
570                 // get next sentence
571                 sal_Int32 nEnd = aBoundary.endPos;
572                 sal_Int32 nI = aBoundary.endPos;
573                 bool bFound = false;
574                 while ( !bFound && ++nI < nLength )
575                 {
576                     implGetSentenceBoundary( sText, aBoundary, nI );
577                     bFound = ( aBoundary.endPos > nEnd );
578                 }
579                 if ( bFound && implIsValidBoundary( aBoundary, nLength ) )
580                 {
581                     aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
582                     aResult.SegmentStart = aBoundary.startPos;
583                     aResult.SegmentEnd = aBoundary.endPos;
584                 }
585             }
586             break;
587             case AccessibleTextType::PARAGRAPH:
588             {
589                 // get paragraph at index
590                 implGetParagraphBoundary( sText, aBoundary, nIndex );
591                 // get next paragraph
592                 if ( aBoundary.endPos < nLength )
593                 {
594                     implGetParagraphBoundary( sText, aBoundary, aBoundary.endPos );
595                     if ( implIsValidBoundary( aBoundary, nLength ) )
596                     {
597                         aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
598                         aResult.SegmentStart = aBoundary.startPos;
599                         aResult.SegmentEnd = aBoundary.endPos;
600                     }
601                 }
602             }
603             break;
604             case AccessibleTextType::LINE:
605             {
606                 // get line at index
607                 implGetLineBoundary( sText, aBoundary, nIndex );
608                 // get next line
609                 if ( aBoundary.endPos < nLength )
610                 {
611                     implGetLineBoundary( sText, aBoundary, aBoundary.endPos );
612                     if ( implIsValidBoundary( aBoundary, nLength ) )
613                     {
614                         aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
615                         aResult.SegmentStart = aBoundary.startPos;
616                         aResult.SegmentEnd = aBoundary.endPos;
617                     }
618                 }
619             }
620             break;
621             case AccessibleTextType::ATTRIBUTE_RUN:
622             {
623                 // TODO: implGetAttributeRunBoundary() (incompatible!)
624             }
625             break;
626             default:
627             {
628                 // unknown text type
629             }
630         }
631 
632         return aResult;
633     }
634 
635 
implInitTextChangedEvent(const OUString & rOldString,const OUString & rNewString,css::uno::Any & rDeleted,css::uno::Any & rInserted)636     bool OCommonAccessibleText::implInitTextChangedEvent(
637         const OUString& rOldString,
638         const OUString& rNewString,
639         css::uno::Any& rDeleted,
640         css::uno::Any& rInserted) // throw()
641     {
642         sal_uInt32 nLenOld = rOldString.getLength();
643         sal_uInt32 nLenNew = rNewString.getLength();
644 
645         // equal
646         if ((0 == nLenOld) && (0 == nLenNew))
647             return false;
648 
649         TextSegment aDeletedText;
650         TextSegment aInsertedText;
651 
652         aDeletedText.SegmentStart = -1;
653         aDeletedText.SegmentEnd = -1;
654         aInsertedText.SegmentStart = -1;
655         aInsertedText.SegmentEnd = -1;
656 
657         // insert only
658         if ((0 == nLenOld) && (nLenNew > 0))
659         {
660             aInsertedText.SegmentStart = 0;
661             aInsertedText.SegmentEnd = nLenNew;
662             aInsertedText.SegmentText = rNewString.copy( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );
663 
664             rInserted <<= aInsertedText;
665             return true;
666         }
667 
668         // delete only
669         if ((nLenOld > 0) && (0 == nLenNew))
670         {
671             aDeletedText.SegmentStart = 0;
672             aDeletedText.SegmentEnd = nLenOld;
673             aDeletedText.SegmentText = rOldString.copy( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );
674 
675             rDeleted <<= aDeletedText;
676             return true;
677         }
678 
679         const sal_Unicode* pFirstDiffOld = rOldString.getStr();
680         const sal_Unicode* pLastDiffOld  = rOldString.getStr() + nLenOld;
681         const sal_Unicode* pFirstDiffNew = rNewString.getStr();
682         const sal_Unicode* pLastDiffNew  = rNewString.getStr() + nLenNew;
683 
684         // find first difference
685         while ((*pFirstDiffOld == *pFirstDiffNew) &&
686                (pFirstDiffOld  <  pLastDiffOld) &&
687                (pFirstDiffNew  <  pLastDiffNew))
688         {
689             pFirstDiffOld++;
690             pFirstDiffNew++;
691         }
692 
693         // equality test
694         if ((0 == *pFirstDiffOld) && (0 == *pFirstDiffNew))
695             return false;
696 
697         // find last difference
698         while ( ( pLastDiffOld > pFirstDiffOld) &&
699                 ( pLastDiffNew > pFirstDiffNew) &&
700                 (pLastDiffOld[-1]  == pLastDiffNew[-1]))
701         {
702             pLastDiffOld--;
703             pLastDiffNew--;
704         }
705 
706         if (pFirstDiffOld < pLastDiffOld)
707         {
708             aDeletedText.SegmentStart = pFirstDiffOld - rOldString.getStr();
709             aDeletedText.SegmentEnd = pLastDiffOld  - rOldString.getStr();
710             aDeletedText.SegmentText = rOldString.copy( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );
711 
712             rDeleted <<= aDeletedText;
713         }
714 
715         if (pFirstDiffNew < pLastDiffNew)
716         {
717             aInsertedText.SegmentStart = pFirstDiffNew - rNewString.getStr();
718             aInsertedText.SegmentEnd = pLastDiffNew  - rNewString.getStr();
719             aInsertedText.SegmentText = rNewString.copy( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );
720 
721             rInserted <<= aInsertedText;
722         }
723         return true;
724     }
725 
726 
727     // OAccessibleTextHelper
728 
729 
OAccessibleTextHelper()730     OAccessibleTextHelper::OAccessibleTextHelper( )
731     {
732     }
733 
734 
735     // XInterface
736 
737 
IMPLEMENT_FORWARD_XINTERFACE2(OAccessibleTextHelper,OAccessibleExtendedComponentHelper,OAccessibleTextHelper_Base)738     IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )
739 
740 
741     // XTypeProvider
742 
743 
744     IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )
745 
746 
747     // XAccessibleText
748 
749 
750     OUString OAccessibleTextHelper::getSelectedText()
751     {
752         OExternalLockGuard aGuard( this );
753 
754         return OCommonAccessibleText::getSelectedText();
755     }
756 
757 
getSelectionStart()758     sal_Int32 OAccessibleTextHelper::getSelectionStart()
759     {
760         OExternalLockGuard aGuard( this );
761 
762         return OCommonAccessibleText::getSelectionStart();
763     }
764 
765 
getSelectionEnd()766     sal_Int32 OAccessibleTextHelper::getSelectionEnd()
767     {
768         OExternalLockGuard aGuard( this );
769 
770         return OCommonAccessibleText::getSelectionEnd();
771     }
772 
773 
getTextAtIndex(sal_Int32 nIndex,sal_Int16 aTextType)774     TextSegment OAccessibleTextHelper::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
775     {
776         OExternalLockGuard aGuard( this );
777 
778         return OCommonAccessibleText::getTextAtIndex( nIndex, aTextType );
779     }
780 
781 
getTextBeforeIndex(sal_Int32 nIndex,sal_Int16 aTextType)782     TextSegment OAccessibleTextHelper::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
783     {
784         OExternalLockGuard aGuard( this );
785 
786         return OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType );
787     }
788 
789 
getTextBehindIndex(sal_Int32 nIndex,sal_Int16 aTextType)790     TextSegment OAccessibleTextHelper::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
791     {
792         OExternalLockGuard aGuard( this );
793 
794         return OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType );
795     }
796 
797 
798 }   // namespace comphelper
799 
800 
801 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
802