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