Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/editeng/source/accessibility/AccessibleStaticTextBase.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
21
// Global header
22
23
24
#include <utility>
25
#include <memory>
26
#include <vector>
27
#include <algorithm>
28
#include <rtl/ustrbuf.hxx>
29
#include <tools/debug.hxx>
30
#include <vcl/svapp.hxx>
31
#include <vcl/unohelp.hxx>
32
#include <comphelper/sequence.hxx>
33
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
34
#include <com/sun/star/uno/Reference.hxx>
35
#include <com/sun/star/awt/Point.hpp>
36
#include <com/sun/star/awt/Rectangle.hpp>
37
#include <com/sun/star/accessibility/AccessibleTextType.hpp>
38
39
40
// Project-local header
41
42
43
#include <editeng/AccessibleStaticTextBase.hxx>
44
45
46
using namespace ::com::sun::star;
47
using namespace ::com::sun::star::accessibility;
48
49
/* TODO:
50
   =====
51
52
   - separate adapter functionality from AccessibleStaticText class
53
54
   - refactor common loops into templates, using mem_fun
55
56
 */
57
58
namespace accessibility
59
{
60
61
typedef std::vector< beans::PropertyValue > PropertyValueVector;
62
63
namespace {
64
65
class PropertyValueEqualFunctor
66
{
67
    const beans::PropertyValue& m_rPValue;
68
69
public:
70
    explicit PropertyValueEqualFunctor(const beans::PropertyValue& rPValue)
71
0
        : m_rPValue(rPValue)
72
0
    {}
73
    bool operator() ( const beans::PropertyValue& rhs ) const
74
0
    {
75
0
        return ( m_rPValue.Name == rhs.Name && m_rPValue.Value == rhs.Value );
76
0
    }
77
};
78
79
}
80
81
sal_Unicode const cNewLine(0x0a);
82
83
84
// Static Helper
85
86
87
static ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
88
                          sal_Int32 nEndPara, sal_Int32 nEndIndex )
89
0
{
90
0
    DBG_ASSERT(nStartPara >= 0 &&
91
0
               nStartIndex >= 0 &&
92
0
               nEndPara >= 0 &&
93
0
               nEndIndex >= 0,
94
0
               "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow");
95
96
0
    return ESelection(nStartPara, nStartIndex, nEndPara, nEndIndex);
97
0
}
98
99
100
AccessibleEditableTextPara& AccessibleStaticTextBase::GetParagraph( sal_Int32 nPara ) const
101
0
{
102
103
0
    if( !mxTextParagraph.is() )
104
0
        throw lang::DisposedException (u"object has been already disposed"_ustr);
105
106
    // TODO: Have a different method on AccessibleEditableTextPara
107
    // that does not care about state changes
108
0
    mxTextParagraph->SetParagraphIndex( nPara );
109
110
0
    return *mxTextParagraph;
111
0
}
112
113
sal_Int32 AccessibleStaticTextBase::GetParagraphCount() const
114
0
{
115
116
0
    if( !mxTextParagraph.is() )
117
0
        return 0;
118
0
    else
119
0
        return mxTextParagraph->GetTextForwarder().GetParagraphCount();
120
0
}
121
122
sal_Int32 AccessibleStaticTextBase::Internal2Index(EPaM nEEIndex) const
123
0
{
124
    // XXX checks for overflow and returns maximum if so
125
0
    sal_Int32 aRes(0);
126
0
    for(sal_Int32 i=0; i<nEEIndex.nPara; ++i)
127
0
    {
128
0
        sal_Int32 nCount = GetParagraph(i).getCharacterCount();
129
0
        if (SAL_MAX_INT32 - aRes > nCount)
130
0
            return SAL_MAX_INT32;
131
0
        aRes += nCount;
132
0
    }
133
134
0
    if (SAL_MAX_INT32 - aRes > nEEIndex.nIndex)
135
0
        return SAL_MAX_INT32;
136
0
    return aRes + nEEIndex.nIndex;
137
0
}
138
139
void AccessibleStaticTextBase::CorrectTextSegment(TextSegment& aTextSegment,
140
                                                  int nPara) const
141
0
{
142
    // Keep 'invalid' values at the TextSegment
143
0
    if( aTextSegment.SegmentStart != -1 &&
144
0
        aTextSegment.SegmentEnd != -1 )
145
0
    {
146
        // #112814# Correct TextSegment by paragraph offset
147
0
        sal_Int32 nOffset(0);
148
0
        int i;
149
0
        for(i=0; i<nPara; ++i)
150
0
            nOffset += GetParagraph(i).getCharacterCount();
151
152
0
        aTextSegment.SegmentStart += nOffset;
153
0
        aTextSegment.SegmentEnd += nOffset;
154
0
    }
155
0
}
156
157
EPaM AccessibleStaticTextBase::ImpCalcInternal(sal_Int32 nFlatIndex, bool bExclusive) const
158
0
{
159
160
0
    if( nFlatIndex < 0 )
161
0
        throw lang::IndexOutOfBoundsException(u"AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds"_ustr);
162
    // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually
163
164
0
    sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount;
165
0
    for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara )
166
0
    {
167
0
        nCurrCount = GetParagraph( nCurrPara ).getCharacterCount();
168
0
        nCurrIndex += nCurrCount;
169
0
        if( nCurrIndex >= nFlatIndex )
170
0
        {
171
            // check overflow
172
0
            DBG_ASSERT(nCurrPara >= 0 &&
173
0
                       nFlatIndex - nCurrIndex + nCurrCount >= 0,
174
0
                       "AccessibleStaticTextBase::Index2Internal: index value overflow");
175
176
0
            return EPaM(nCurrPara, nFlatIndex - nCurrIndex + nCurrCount);
177
0
        }
178
0
    }
179
180
    // #102170# Allow one-past the end for ranges
181
0
    if( bExclusive && nCurrIndex == nFlatIndex )
182
0
    {
183
        // check overflow
184
0
        DBG_ASSERT(nCurrPara > 0 &&
185
0
                   nFlatIndex - nCurrIndex + nCurrCount >= 0,
186
0
                   "AccessibleStaticTextBase::Index2Internal: index value overflow");
187
188
0
        return EPaM(nCurrPara - 1, nFlatIndex - nCurrIndex + nCurrCount);
189
0
    }
190
191
    // not found? Out of bounds
192
0
    throw lang::IndexOutOfBoundsException(u"AccessibleStaticTextBase::Index2Internal: character index out of bounds"_ustr);
193
0
}
194
195
bool AccessibleStaticTextBase::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
196
                                                      sal_Int32 nEndPara, sal_Int32 nEndIndex )
197
0
{
198
199
0
    if( !mxTextParagraph.is() )
200
0
        return false;
201
202
0
    try
203
0
    {
204
0
        SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
205
0
        return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
206
0
    }
207
0
    catch( const uno::RuntimeException& )
208
0
    {
209
0
        return false;
210
0
    }
211
0
}
212
213
bool AccessibleStaticTextBase::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
214
                                                  sal_Int32 nEndPara, sal_Int32 nEndIndex )
215
0
{
216
217
0
    if( !mxTextParagraph.is() )
218
0
        return false;
219
220
0
    try
221
0
    {
222
0
        SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
223
0
        mxTextParagraph->GetTextForwarder();    // MUST be after GetEditViewForwarder(), see method docs
224
0
        bool aRetVal;
225
226
        // save current selection
227
0
        ESelection aOldSelection;
228
229
0
        rCacheVF.GetSelection( aOldSelection );
230
0
        rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
231
0
        aRetVal = rCacheVF.Copy();
232
0
        rCacheVF.SetSelection( aOldSelection ); // restore
233
234
0
        return aRetVal;
235
0
    }
236
0
    catch( const uno::RuntimeException& )
237
0
    {
238
0
        return false;
239
0
    }
240
0
}
241
242
//the input argument is the index(including "\n" ) in the string.
243
//the function will calculate the actual index(not including "\n") in the string.
244
//and return true if the index is just at a "\n"
245
bool AccessibleStaticTextBase::RemoveLineBreakCount( sal_Int32& rIndex )
246
0
{
247
    // get the total char number inside the cell.
248
0
    sal_Int32 i, nCount, nParas;
249
0
    for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i )
250
0
        nCount += GetParagraph(i).getCharacterCount();
251
0
    nCount = nCount + (nParas-1);
252
0
    if( nCount == 0 &&  rIndex == 0) return false;
253
254
255
0
    sal_Int32 nCurrPara, nCurrCount;
256
0
    sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0;
257
0
    sal_Int32 nParaCount = GetParagraphCount();
258
0
    for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ )
259
0
    {
260
0
        nCurrCount += GetParagraph( nCurrPara ).getCharacterCount();
261
0
        nLineBreakPos = nCurrCount++;
262
0
        if ( rIndex == nLineBreakPos )
263
0
        {
264
0
            rIndex -= (++nLineBreakCount);//(++nLineBreakCount);
265
0
            if ( rIndex < 0)
266
0
            {
267
0
                rIndex = 0;
268
0
            }
269
            //if the index is at the last position of the last paragraph
270
            //there is no "\n" , so we should increase rIndex by 1 and return false.
271
0
            if ( (nCurrPara+1) == nParaCount )
272
0
            {
273
0
                rIndex++;
274
0
                return false;
275
0
            }
276
0
            else
277
0
            {
278
0
                return true;
279
0
            }
280
0
        }
281
0
        else if ( rIndex < nLineBreakPos )
282
0
        {
283
0
            rIndex -= nLineBreakCount;
284
0
            return false;
285
0
        }
286
0
        else
287
0
        {
288
0
            nLineBreakCount++;
289
0
        }
290
0
    }
291
0
    return false;
292
0
}
293
294
AccessibleStaticTextBase::AccessibleStaticTextBase(std::unique_ptr<SvxEditSource>&& pEditSource)
295
    // TODO: this is still somewhat of a hack, all the more since
296
    // now the maTextParagraph has an empty parent reference set
297
0
    : mxTextParagraph(new AccessibleEditableTextPara(nullptr))
298
0
{
299
0
    SolarMutexGuard aGuard;
300
301
0
    SetEditSource( std::move(pEditSource) );
302
0
}
303
304
AccessibleStaticTextBase::~AccessibleStaticTextBase()
305
0
{
306
0
}
307
308
void AccessibleStaticTextBase::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
309
0
{
310
    // precondition: solar mutex locked
311
0
    DBG_TESTSOLARMUTEX();
312
313
0
    maEditSource.SetEditSource(std::move(pEditSource));
314
0
    if (mxTextParagraph.is())
315
0
        mxTextParagraph->SetEditSource(&maEditSource);
316
0
}
317
318
void AccessibleStaticTextBase::SetOffset( const Point& rPoint )
319
0
{
320
    // precondition: solar mutex locked
321
0
    DBG_TESTSOLARMUTEX();
322
323
0
    if (mxTextParagraph.is())
324
0
        mxTextParagraph->SetEEOffset(rPoint);
325
0
}
326
327
void AccessibleStaticTextBase::Dispose()
328
0
{
329
    // we're the owner of the paragraph, so destroy it, too
330
0
    if (mxTextParagraph.is())
331
0
        mxTextParagraph->dispose();
332
333
    // drop reference
334
0
    mxTextParagraph.clear();
335
0
}
336
337
// XAccessibleContext
338
sal_Int64 AccessibleStaticTextBase::getAccessibleChildCount()
339
0
{
340
    // no children at all
341
0
    return 0;
342
0
}
343
344
uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int64 /*i*/ )
345
0
{
346
    // no children at all
347
0
    return uno::Reference< XAccessible >();
348
0
}
349
350
uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ )
351
0
{
352
    // no children at all
353
0
    return uno::Reference< XAccessible >();
354
0
}
355
356
// XAccessibleText
357
sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition()
358
0
{
359
0
    SolarMutexGuard aGuard;
360
361
0
    sal_Int32 i, nPos, nParas;
362
0
    for (i = 0, nPos = -1, nParas = GetParagraphCount(); i<nParas; ++i )
363
0
    {
364
0
        if ((nPos = GetParagraph(i).getCaretPosition()) != -1)
365
0
            return nPos;
366
0
    }
367
368
0
    return nPos;
369
0
}
370
371
sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex )
372
0
{
373
0
    return setSelection(nIndex, nIndex);
374
0
}
375
376
sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex )
377
0
{
378
0
    SolarMutexGuard aGuard;
379
380
0
    EPaM aPos(Index2Internal(nIndex));
381
382
0
    return GetParagraph(aPos.nPara).getCharacter(aPos.nIndex);
383
0
}
384
385
uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
386
0
{
387
0
    SolarMutexGuard aGuard;
388
389
    //get the actual index without "\n"
390
0
    RemoveLineBreakCount(nIndex);
391
392
0
    EPaM aPos(Index2Internal(nIndex));
393
394
0
    return GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes );
395
0
}
396
397
awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex )
398
0
{
399
0
    SolarMutexGuard aGuard;
400
401
    // #108900# Allow ranges for nIndex, as one-past-the-end
402
    // values are now legal, too.
403
0
    EPaM aPos(Range2Internal(nIndex));
404
405
    // #i70916# Text in spread sheet cells return the wrong extents
406
0
    AccessibleEditableTextPara& rPara = GetParagraph( aPos.nPara );
407
0
    awt::Rectangle aParaBounds( rPara.getBounds() );
408
0
    awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) );
409
0
    aBounds.X += aParaBounds.X;
410
0
    aBounds.Y += aParaBounds.Y;
411
412
0
    return aBounds;
413
0
}
414
415
sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount()
416
0
{
417
0
    SolarMutexGuard aGuard;
418
419
0
    sal_Int32 i, nCount, nParas;
420
0
    for (i = 0, nCount = 0, nParas = GetParagraphCount(); i < nParas; ++i)
421
0
        nCount += GetParagraph(i).getCharacterCount();
422
    //count on the number of "\n" which equals number of paragraphs decrease 1.
423
0
    nCount = nCount + (nParas-1);
424
0
    return nCount;
425
0
}
426
427
sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint )
428
0
{
429
0
    SolarMutexGuard aGuard;
430
431
0
    const sal_Int32 nParas(GetParagraphCount());
432
0
    sal_Int32 nIndex;
433
0
    int i;
434
0
    for( i=0; i<nParas; ++i )
435
0
    {
436
        // TODO: maybe exploit the fact that paragraphs are
437
        // ordered vertically for early exit
438
439
        // #i70916# Text in spread sheet cells return the wrong extents
440
0
        AccessibleEditableTextPara& rPara = GetParagraph(i);
441
0
        awt::Rectangle aParaBounds( rPara.getBounds() );
442
0
        awt::Point aPoint( rPoint );
443
0
        aPoint.X -= aParaBounds.X;
444
0
        aPoint.Y -= aParaBounds.Y;
445
446
        // #112814# Use correct index offset
447
0
        if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 )
448
0
            return Internal2Index(EPaM(i, nIndex));
449
0
    }
450
451
0
    return -1;
452
0
}
453
454
OUString SAL_CALL AccessibleStaticTextBase::getSelectedText()
455
0
{
456
0
    SolarMutexGuard aGuard;
457
458
0
    sal_Int32 nStart( getSelectionStart() );
459
0
    sal_Int32 nEnd( getSelectionEnd() );
460
461
    // #104481# Return the empty string for 'no selection'
462
0
    if( nStart < 0 || nEnd < 0 )
463
0
        return OUString();
464
465
0
    return getTextRange( nStart, nEnd );
466
0
}
467
468
sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart()
469
0
{
470
0
    SolarMutexGuard aGuard;
471
472
0
    sal_Int32 i, nPos, nParas;
473
0
    for (i = 0, nPos = -1, nParas = GetParagraphCount(); i < nParas; ++i)
474
0
    {
475
0
        if ((nPos=GetParagraph(i).getSelectionStart()) != -1)
476
0
            return nPos;
477
0
    }
478
479
0
    return nPos;
480
0
}
481
482
sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd()
483
0
{
484
0
    SolarMutexGuard aGuard;
485
486
0
    sal_Int32 i, nPos, nParas;
487
0
    for (i = 0, nPos = -1, nParas = GetParagraphCount(); i < nParas; ++i)
488
0
    {
489
0
        if ((nPos = GetParagraph(i).getSelectionEnd()) != -1)
490
0
            return nPos;
491
0
    }
492
493
0
    return nPos;
494
0
}
495
496
sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
497
0
{
498
0
    SolarMutexGuard aGuard;
499
500
0
    EPaM aStartIndex(Range2Internal(nStartIndex));
501
0
    EPaM aEndIndex(Range2Internal(nEndIndex));
502
503
0
    return SetSelection(aStartIndex.nPara, aStartIndex.nIndex,
504
0
                        aEndIndex.nPara, aEndIndex.nIndex);
505
0
}
506
507
OUString SAL_CALL AccessibleStaticTextBase::getText()
508
0
{
509
0
    SolarMutexGuard aGuard;
510
511
0
    sal_Int32 i, nParas;
512
0
    OUStringBuffer aRes;
513
0
    for (i = 0, nParas = GetParagraphCount(); i < nParas; ++i)
514
0
        aRes.append(GetParagraph(i).getText());
515
516
0
    return aRes.makeStringAndClear();
517
0
}
518
519
OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
520
0
{
521
0
    SolarMutexGuard aGuard;
522
523
0
    if( nStartIndex > nEndIndex )
524
0
        std::swap(nStartIndex, nEndIndex);
525
    //if startindex equals endindex we will get nothing. So return an empty string directly.
526
0
    if ( nStartIndex == nEndIndex )
527
0
    {
528
0
        return OUString();
529
0
    }
530
0
    bool bStart = RemoveLineBreakCount(nStartIndex);
531
    //if the start index is just at a "\n", we need to begin from the next char
532
0
    if ( bStart )
533
0
    {
534
0
        nStartIndex++;
535
0
    }
536
    //we need to find out whether the previous position of the current endindex is at "\n" or not
537
    //if yes we need to mark it and add "\n" at the end of the result
538
0
    sal_Int32 nTemp = nEndIndex - 1;
539
0
    bool bEnd = RemoveLineBreakCount(nTemp);
540
0
    bool bTemp = RemoveLineBreakCount(nEndIndex);
541
    //if the below condition is true it indicates an empty paragraph with just a "\n"
542
    //so we need to set one "\n" flag to avoid duplication.
543
0
    if ( bStart && bEnd && ( nStartIndex == nEndIndex) )
544
0
    {
545
0
        bEnd = false;
546
0
    }
547
    //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure
548
    //the char before "\n" is included. Because string returned by this function will not include
549
    //the char at the endindex.
550
0
    if ( bTemp )
551
0
    {
552
0
        nEndIndex++;
553
0
    }
554
0
    OUStringBuffer aRes;
555
0
    EPaM aStartIndex(Range2Internal(nStartIndex));
556
0
    EPaM aEndIndex(Range2Internal(nEndIndex));
557
558
    // #102170# Special case: start and end paragraph are identical
559
0
    if( aStartIndex.nPara == aEndIndex.nPara )
560
0
    {
561
        //we don't return the string directly now for that we have to do some further process for "\n"
562
0
        aRes = GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex );
563
0
    }
564
0
    else
565
0
    {
566
0
        sal_Int32 i( aStartIndex.nPara );
567
0
        aRes = GetParagraph(i).getTextRange(aStartIndex.nIndex,
568
0
                                            GetParagraph(i).getCharacterCount()/*-1*/);
569
0
        ++i;
570
571
        // paragraphs inbetween are fully included
572
0
        for( ; i<aEndIndex.nPara; ++i )
573
0
        {
574
0
            aRes.append(OUStringChar(cNewLine) + GetParagraph(i).getText());
575
0
        }
576
577
0
        if( i<=aEndIndex.nPara )
578
0
        {
579
            //if the below condition is matched it means that endindex is at mid of the last paragraph
580
            //we need to add a "\n" before we add the last part of the string.
581
0
            if ( !bEnd && aEndIndex.nIndex )
582
0
            {
583
0
                aRes.append(cNewLine);
584
0
            }
585
0
            aRes.append(GetParagraph(i).getTextRange( 0, aEndIndex.nIndex ));
586
0
        }
587
0
    }
588
    //According to the flag we marked before, we have to add "\n" at the beginning
589
    //or at the end of the result string.
590
0
    if ( bStart )
591
0
    {
592
0
        aRes.insert(0, OUStringChar(cNewLine));
593
0
    }
594
0
    if ( bEnd )
595
0
    {
596
0
        aRes.append(OUStringChar(cNewLine));
597
0
    }
598
0
    return aRes.makeStringAndClear();
599
0
}
600
601
css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
602
0
{
603
0
    SolarMutexGuard aGuard;
604
605
0
    bool bLineBreak = RemoveLineBreakCount(nIndex);
606
0
    EPaM aPos(Range2Internal(nIndex));
607
608
0
    css::accessibility::TextSegment aResult;
609
610
0
    if( AccessibleTextType::PARAGRAPH == aTextType )
611
0
    {
612
        // #106393# Special casing one behind last paragraph is
613
        // not necessary, since then, we return the content and
614
        // boundary of that last paragraph. Range2Internal is
615
        // tolerant against that, and returns the last paragraph
616
        // in aPos.nPara.
617
618
        // retrieve full text of the paragraph
619
0
        aResult.SegmentText = GetParagraph( aPos.nPara ).getText();
620
621
        // #112814# Adapt the start index with the paragraph offset
622
0
        aResult.SegmentStart = Internal2Index(EPaM(aPos.nPara, 0));
623
0
        aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
624
0
    }
625
0
    else if ( AccessibleTextType::ATTRIBUTE_RUN == aTextType )
626
0
    {
627
0
        SvxAccessibleTextAdapter& rTextForwarder = GetParagraph( aPos.nIndex ).GetTextForwarder();
628
0
        sal_Int32 nStartIndex, nEndIndex;
629
0
        if ( rTextForwarder.GetAttributeRun( nStartIndex, nEndIndex, aPos.nPara, aPos.nIndex, true ) )
630
0
        {
631
0
            aResult.SegmentText = getTextRange( nStartIndex, nEndIndex );
632
0
            aResult.SegmentStart = nStartIndex;
633
0
            aResult.SegmentEnd = nEndIndex;
634
0
        }
635
0
    }
636
0
    else
637
0
    {
638
        // No special handling required, forward to wrapped class
639
0
        aResult = GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType );
640
641
        // #112814# Adapt the start index with the paragraph offset
642
0
        CorrectTextSegment( aResult, aPos.nPara );
643
0
        if ( bLineBreak )
644
0
        {
645
0
            aResult.SegmentText = OUString(cNewLine);
646
0
        }
647
0
    }
648
649
0
    return aResult;
650
0
}
651
652
css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
653
0
{
654
0
    SolarMutexGuard aGuard;
655
656
0
    sal_Int32 nOldIdx = nIndex;
657
0
    bool bLineBreak =  RemoveLineBreakCount(nIndex);
658
0
    EPaM aPos(Range2Internal(nIndex));
659
660
0
    css::accessibility::TextSegment aResult;
661
662
0
    if( AccessibleTextType::PARAGRAPH == aTextType )
663
0
    {
664
0
        if( aPos.nIndex == GetParagraph( aPos.nPara ).getCharacterCount() )
665
0
        {
666
            // #103589# Special casing one behind the last paragraph
667
0
            aResult.SegmentText = GetParagraph( aPos.nPara ).getText();
668
669
            // #112814# Adapt the start index with the paragraph offset
670
0
            aResult.SegmentStart = Internal2Index(EPaM(aPos.nPara, 0));
671
0
        }
672
0
        else if( aPos.nPara > 0 )
673
0
        {
674
0
            aResult.SegmentText = GetParagraph( aPos.nPara - 1 ).getText();
675
676
            // #112814# Adapt the start index with the paragraph offset
677
0
            aResult.SegmentStart = Internal2Index(EPaM(aPos.nPara - 1, 0));
678
0
        }
679
680
0
        aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
681
0
    }
682
0
    else
683
0
    {
684
        // No special handling required, forward to wrapped class
685
0
        aResult = GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType );
686
687
        // #112814# Adapt the start index with the paragraph offset
688
0
        CorrectTextSegment( aResult, aPos.nPara );
689
0
        if ( bLineBreak && (nOldIdx-1) >= 0)
690
0
        {
691
0
            aResult = getTextAtIndex( nOldIdx-1, aTextType );
692
0
        }
693
0
    }
694
695
0
    return aResult;
696
0
}
697
698
css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
699
0
{
700
0
    SolarMutexGuard aGuard;
701
702
0
    sal_Int32 nTemp = nIndex+1;
703
0
    bool bLineBreak = RemoveLineBreakCount(nTemp);
704
0
    RemoveLineBreakCount(nIndex);
705
0
    EPaM aPos(Range2Internal(nIndex));
706
707
0
    css::accessibility::TextSegment aResult;
708
709
0
    if( AccessibleTextType::PARAGRAPH == aTextType )
710
0
    {
711
        // Special casing one behind the last paragraph is not
712
        // necessary, this case is invalid here for
713
        // getTextBehindIndex
714
0
        if (aPos.nPara + 1 < GetParagraphCount())
715
0
        {
716
0
            aResult.SegmentText = GetParagraph( aPos.nPara + 1 ).getText();
717
718
            // #112814# Adapt the start index with the paragraph offset
719
0
            aResult.SegmentStart = Internal2Index(EPaM(aPos.nPara + 1, 0));
720
0
            aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
721
0
        }
722
0
    }
723
0
    else
724
0
    {
725
        // No special handling required, forward to wrapped class
726
0
        aResult = GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType );
727
728
        // #112814# Adapt the start index with the paragraph offset
729
0
        CorrectTextSegment( aResult, aPos.nPara );
730
0
        if ( bLineBreak )
731
0
        {
732
0
            aResult.SegmentText = OUStringChar(cNewLine) + aResult.SegmentText;
733
0
        }
734
0
   }
735
736
0
    return aResult;
737
0
}
738
739
sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
740
0
{
741
0
    SolarMutexGuard aGuard;
742
743
0
    if( nStartIndex > nEndIndex )
744
0
        std::swap(nStartIndex, nEndIndex);
745
746
0
    EPaM aStartIndex(Range2Internal(nStartIndex));
747
0
    EPaM aEndIndex(Range2Internal(nEndIndex));
748
749
0
    return CopyText(aStartIndex.nPara, aStartIndex.nIndex, aEndIndex.nPara, aEndIndex.nIndex);
750
0
}
751
752
sal_Bool SAL_CALL AccessibleStaticTextBase::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
753
0
{
754
0
    return false;
755
0
}
756
757
// XAccessibleTextAttributes
758
uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< OUString >& RequestedAttributes )
759
0
{
760
    // get the intersection of the default attributes of all paragraphs
761
762
0
    SolarMutexGuard aGuard;
763
764
0
    PropertyValueVector aDefAttrVec(
765
0
            comphelper::sequenceToContainer<PropertyValueVector>(GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes )) );
766
767
0
    const sal_Int32 nParaCount = GetParagraphCount();
768
0
    for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara )
769
0
    {
770
0
        uno::Sequence< beans::PropertyValue > aSeq = GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes );
771
0
        PropertyValueVector aIntersectionVec;
772
773
0
        for ( const auto& rDefAttr : aDefAttrVec )
774
0
        {
775
0
            auto it = std::find_if(aSeq.begin(), aSeq.end(), PropertyValueEqualFunctor(rDefAttr));
776
0
            if (it != aSeq.end())
777
0
                aIntersectionVec.push_back(*it);
778
0
        }
779
780
0
        aDefAttrVec.swap( aIntersectionVec );
781
782
0
        if ( aDefAttrVec.empty() )
783
0
        {
784
0
            break;
785
0
        }
786
0
    }
787
788
0
    return comphelper::containerToSequence(aDefAttrVec);
789
0
}
790
791
uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< OUString >& RequestedAttributes )
792
0
{
793
    // get those default attributes of the paragraph, which are not part
794
    // of the intersection of all paragraphs and add them to the run attributes
795
796
0
    SolarMutexGuard aGuard;
797
798
0
    EPaM aPos(Index2Internal(nIndex));
799
0
    AccessibleEditableTextPara& rPara = GetParagraph( aPos.nPara );
800
0
    uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes );
801
0
    uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes );
802
0
    uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes );
803
0
    PropertyValueVector aDiffVec;
804
805
0
    for (auto& defAttr : aDefAttrSeq)
806
0
    {
807
0
        bool bNone = std::none_of(aIntersectionSeq.begin(), aIntersectionSeq.end(),
808
0
                                  PropertyValueEqualFunctor(defAttr));
809
0
        if (bNone && defAttr.Handle != 0)
810
0
        {
811
0
            aDiffVec.push_back(defAttr);
812
0
        }
813
0
    }
814
815
0
    return comphelper::concatSequences(aRunAttrSeq, aDiffVec);
816
0
}
817
818
tools::Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const
819
0
{
820
0
    if (!mxTextParagraph.is())
821
0
        return tools::Rectangle();
822
823
0
    awt::Rectangle aAwtRect = mxTextParagraph->getBounds();
824
0
    return vcl::unohelper::ConvertToVCLRect(aAwtRect);
825
0
}
826
827
}  // end of namespace accessibility
828
829
830
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */