Coverage Report

Created: 2025-12-31 10:39

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