bump product version to 6.4.0.3
[LibreOffice.git] / editeng / source / accessibility / AccessibleStaticTextBase.cxx
blobc85cc3c9a589a72d50ceede3e517dd7c3e9c00bf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 // Global header
24 #include <limits.h>
25 #include <utility>
26 #include <memory>
27 #include <vector>
28 #include <algorithm>
29 #include <functional>
30 #include <tools/debug.hxx>
31 #include <vcl/window.hxx>
32 #include <vcl/svapp.hxx>
33 #include <comphelper/sequence.hxx>
34 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
35 #include <com/sun/star/uno/Reference.hxx>
36 #include <com/sun/star/awt/Point.hpp>
37 #include <com/sun/star/awt/Rectangle.hpp>
38 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
41 // Project-local header
44 #include <editeng/editdata.hxx>
45 #include <editeng/unopracc.hxx>
46 #include <editeng/unoedprx.hxx>
47 #include <editeng/AccessibleStaticTextBase.hxx>
48 #include <editeng/AccessibleEditableTextPara.hxx>
51 using namespace ::com::sun::star;
52 using namespace ::com::sun::star::accessibility;
54 /* TODO:
55 =====
57 - separate adapter functionality from AccessibleStaticText class
59 - refactor common loops into templates, using mem_fun
63 namespace accessibility
65 typedef std::vector< beans::PropertyValue > PropertyValueVector;
67 class PropertyValueEqualFunctor
69 const beans::PropertyValue& m_rPValue;
71 public:
72 explicit PropertyValueEqualFunctor(const beans::PropertyValue& rPValue)
73 : m_rPValue(rPValue)
75 bool operator() ( const beans::PropertyValue& rhs ) const
77 return ( m_rPValue.Name == rhs.Name && m_rPValue.Value == rhs.Value );
80 sal_Unicode const cNewLine(0x0a);
83 // Static Helper
86 static ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
87 sal_Int32 nEndPara, sal_Int32 nEndIndex )
89 DBG_ASSERT(nStartPara >= 0 &&
90 nStartIndex >= 0 &&
91 nEndPara >= 0 &&
92 nEndIndex >= 0,
93 "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow");
95 return ESelection(nStartPara, nStartIndex, nEndPara, nEndIndex);
99 // AccessibleStaticTextBase_Impl declaration
102 /** AccessibleStaticTextBase_Impl
104 This class implements the AccessibleStaticTextBase
105 functionality, mainly by forwarding the calls to an aggregated
106 AccessibleEditableTextPara. As this is a therefore non-trivial
107 adapter, factoring out the common functionality from
108 AccessibleEditableTextPara might be a profitable future task.
110 class AccessibleStaticTextBase_Impl
112 friend class AccessibleStaticTextBase;
113 public:
115 // receive pointer to our frontend class and view window
116 AccessibleStaticTextBase_Impl();
118 void SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource );
120 void SetEventSource( const uno::Reference< XAccessible >& rInterface )
123 mxThis = rInterface;
126 void SetOffset( const Point& );
128 void Dispose();
130 AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const;
131 sal_Int32 GetParagraphCount() const;
133 EPosition Index2Internal( sal_Int32 nFlatIndex ) const
136 return ImpCalcInternal( nFlatIndex, false );
139 EPosition Range2Internal( sal_Int32 nFlatIndex ) const
142 return ImpCalcInternal( nFlatIndex, true );
145 sal_Int32 Internal2Index( EPosition nEEIndex ) const;
147 void CorrectTextSegment( TextSegment& aTextSegment,
148 int nPara ) const;
150 bool SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
151 sal_Int32 nEndPara, sal_Int32 nEndIndex );
152 bool CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
153 sal_Int32 nEndPara, sal_Int32 nEndIndex );
155 tools::Rectangle GetParagraphBoundingBox() const;
156 bool RemoveLineBreakCount( sal_Int32& rIndex );
158 private:
160 EPosition ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const;
162 // our frontend class (the one implementing the actual
163 // interface). That's not necessarily the one containing the impl
164 // pointer
165 uno::Reference< XAccessible > mxThis;
167 // implements our functionality, we're just an adapter (guarded by solar mutex)
168 mutable rtl::Reference<AccessibleEditableTextPara> mxTextParagraph;
170 // a wrapper for the text forwarders (guarded by solar mutex)
171 mutable SvxEditSourceAdapter maEditSource;
175 // AccessibleStaticTextBase_Impl implementation
178 AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl() :
179 mxTextParagraph( new AccessibleEditableTextPara(nullptr) ),
180 maEditSource()
183 // TODO: this is still somewhat of a hack, all the more since
184 // now the maTextParagraph has an empty parent reference set
187 void AccessibleStaticTextBase_Impl::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
190 maEditSource.SetEditSource( std::move(pEditSource) );
191 if( mxTextParagraph.is() )
192 mxTextParagraph->SetEditSource( &maEditSource );
195 void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint )
197 if( mxTextParagraph.is() )
198 mxTextParagraph->SetEEOffset( rPoint );
201 void AccessibleStaticTextBase_Impl::Dispose()
204 // we're the owner of the paragraph, so destroy it, too
205 if( mxTextParagraph.is() )
206 mxTextParagraph->Dispose();
208 // drop references
209 mxThis = nullptr;
210 mxTextParagraph.clear();
213 AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const
216 if( !mxTextParagraph.is() )
217 throw lang::DisposedException ("object has been already disposed", mxThis );
219 // TODO: Have a different method on AccessibleEditableTextPara
220 // that does not care about state changes
221 mxTextParagraph->SetParagraphIndex( nPara );
223 return *mxTextParagraph;
226 sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const
229 if( !mxTextParagraph.is() )
230 return 0;
231 else
232 return mxTextParagraph->GetTextForwarder().GetParagraphCount();
235 sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index( EPosition nEEIndex ) const
237 // XXX checks for overflow and returns maximum if so
238 sal_Int32 aRes(0);
239 for(sal_Int32 i=0; i<nEEIndex.nPara; ++i)
241 sal_Int32 nCount = GetParagraph(i).getCharacterCount();
242 if (SAL_MAX_INT32 - aRes > nCount)
243 return SAL_MAX_INT32;
244 aRes += nCount;
247 if (SAL_MAX_INT32 - aRes > nEEIndex.nIndex)
248 return SAL_MAX_INT32;
249 return aRes + nEEIndex.nIndex;
252 void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment& aTextSegment,
253 int nPara ) const
255 // Keep 'invalid' values at the TextSegment
256 if( aTextSegment.SegmentStart != -1 &&
257 aTextSegment.SegmentEnd != -1 )
259 // #112814# Correct TextSegment by paragraph offset
260 sal_Int32 nOffset(0);
261 int i;
262 for(i=0; i<nPara; ++i)
263 nOffset += GetParagraph(i).getCharacterCount();
265 aTextSegment.SegmentStart += nOffset;
266 aTextSegment.SegmentEnd += nOffset;
270 EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const
273 if( nFlatIndex < 0 )
274 throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
275 mxThis);
276 // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually
278 sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount;
279 for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara )
281 nCurrCount = GetParagraph( nCurrPara ).getCharacterCount();
282 nCurrIndex += nCurrCount;
283 if( nCurrIndex >= nFlatIndex )
285 // check overflow
286 DBG_ASSERT(nCurrPara >= 0 &&
287 nFlatIndex - nCurrIndex + nCurrCount >= 0,
288 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
290 return EPosition(nCurrPara, nFlatIndex - nCurrIndex + nCurrCount);
294 // #102170# Allow one-past the end for ranges
295 if( bExclusive && nCurrIndex == nFlatIndex )
297 // check overflow
298 DBG_ASSERT(nCurrPara > 0 &&
299 nFlatIndex - nCurrIndex + nCurrCount >= 0,
300 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
302 return EPosition(nCurrPara-1, nFlatIndex - nCurrIndex + nCurrCount);
305 // not found? Out of bounds
306 throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
307 mxThis);
310 bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
311 sal_Int32 nEndPara, sal_Int32 nEndIndex )
314 if( !mxTextParagraph.is() )
315 return false;
319 SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
320 return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
322 catch( const uno::RuntimeException& )
324 return false;
328 bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
329 sal_Int32 nEndPara, sal_Int32 nEndIndex )
332 if( !mxTextParagraph.is() )
333 return false;
337 SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
338 mxTextParagraph->GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
339 bool aRetVal;
341 // save current selection
342 ESelection aOldSelection;
344 rCacheVF.GetSelection( aOldSelection );
345 rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
346 aRetVal = rCacheVF.Copy();
347 rCacheVF.SetSelection( aOldSelection ); // restore
349 return aRetVal;
351 catch( const uno::RuntimeException& )
353 return false;
357 tools::Rectangle AccessibleStaticTextBase_Impl::GetParagraphBoundingBox() const
359 tools::Rectangle aRect;
360 if( mxTextParagraph.is() )
362 awt::Rectangle aAwtRect = mxTextParagraph->getBounds();
363 aRect = tools::Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) );
365 else
367 aRect.SetEmpty();
369 return aRect;
371 //the input argument is the index(including "\n" ) in the string.
372 //the function will calculate the actual index(not including "\n") in the string.
373 //and return true if the index is just at a "\n"
374 bool AccessibleStaticTextBase_Impl::RemoveLineBreakCount( sal_Int32& rIndex )
376 // get the total char number inside the cell.
377 sal_Int32 i, nCount, nParas;
378 for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i )
379 nCount += GetParagraph(i).getCharacterCount();
380 nCount = nCount + (nParas-1);
381 if( nCount == 0 && rIndex == 0) return false;
384 sal_Int32 nCurrPara, nCurrCount;
385 sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0;
386 sal_Int32 nParaCount = GetParagraphCount();
387 for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ )
389 nCurrCount += GetParagraph( nCurrPara ).getCharacterCount();
390 nLineBreakPos = nCurrCount++;
391 if ( rIndex == nLineBreakPos )
393 rIndex -= (++nLineBreakCount);//(++nLineBreakCount);
394 if ( rIndex < 0)
396 rIndex = 0;
398 //if the index is at the last position of the last paragraph
399 //there is no "\n" , so we should increase rIndex by 1 and return false.
400 if ( (nCurrPara+1) == nParaCount )
402 rIndex++;
403 return false;
405 else
407 return true;
410 else if ( rIndex < nLineBreakPos )
412 rIndex -= nLineBreakCount;
413 return false;
415 else
417 nLineBreakCount++;
420 return false;
424 // AccessibleStaticTextBase implementation
426 AccessibleStaticTextBase::AccessibleStaticTextBase( std::unique_ptr< SvxEditSource > && pEditSource ) :
427 mpImpl( new AccessibleStaticTextBase_Impl() )
429 SolarMutexGuard aGuard;
431 SetEditSource( std::move(pEditSource) );
434 AccessibleStaticTextBase::~AccessibleStaticTextBase()
438 void AccessibleStaticTextBase::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
440 // precondition: solar mutex locked
441 DBG_TESTSOLARMUTEX();
443 mpImpl->SetEditSource( std::move(pEditSource) );
446 void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface )
448 mpImpl->SetEventSource( rInterface );
452 void AccessibleStaticTextBase::SetOffset( const Point& rPoint )
454 // precondition: solar mutex locked
455 DBG_TESTSOLARMUTEX();
457 mpImpl->SetOffset( rPoint );
460 void AccessibleStaticTextBase::Dispose()
462 mpImpl->Dispose();
466 // XAccessibleContext
467 sal_Int32 AccessibleStaticTextBase::getAccessibleChildCount()
469 // no children at all
470 return 0;
473 uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int32 /*i*/ )
475 // no children at all
476 return uno::Reference< XAccessible >();
479 uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ )
481 // no children at all
482 return uno::Reference< XAccessible >();
485 // XAccessibleText
486 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition()
488 SolarMutexGuard aGuard;
490 sal_Int32 i, nPos, nParas;
491 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
493 if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 )
494 return nPos;
497 return nPos;
500 sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex )
502 return setSelection(nIndex, nIndex);
505 sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex )
507 SolarMutexGuard aGuard;
509 EPosition aPos( mpImpl->Index2Internal(nIndex) );
511 return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex );
514 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
516 SolarMutexGuard aGuard;
518 //get the actual index without "\n"
519 mpImpl->RemoveLineBreakCount( nIndex );
521 EPosition aPos( mpImpl->Index2Internal(nIndex) );
523 return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes );
526 awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex )
528 SolarMutexGuard aGuard;
530 // #108900# Allow ranges for nIndex, as one-past-the-end
531 // values are now legal, too.
532 EPosition aPos( mpImpl->Range2Internal(nIndex) );
534 // #i70916# Text in spread sheet cells return the wrong extents
535 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
536 awt::Rectangle aParaBounds( rPara.getBounds() );
537 awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) );
538 aBounds.X += aParaBounds.X;
539 aBounds.Y += aParaBounds.Y;
541 return aBounds;
544 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount()
546 SolarMutexGuard aGuard;
548 sal_Int32 i, nCount, nParas;
549 for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
550 nCount += mpImpl->GetParagraph(i).getCharacterCount();
551 //count on the number of "\n" which equals number of paragraphs decrease 1.
552 nCount = nCount + (nParas-1);
553 return nCount;
556 sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint )
558 SolarMutexGuard aGuard;
560 const sal_Int32 nParas( mpImpl->GetParagraphCount() );
561 sal_Int32 nIndex;
562 int i;
563 for( i=0; i<nParas; ++i )
565 // TODO: maybe exploit the fact that paragraphs are
566 // ordered vertically for early exit
568 // #i70916# Text in spread sheet cells return the wrong extents
569 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i );
570 awt::Rectangle aParaBounds( rPara.getBounds() );
571 awt::Point aPoint( rPoint );
572 aPoint.X -= aParaBounds.X;
573 aPoint.Y -= aParaBounds.Y;
575 // #112814# Use correct index offset
576 if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 )
577 return mpImpl->Internal2Index(EPosition(i, nIndex));
580 return -1;
583 OUString SAL_CALL AccessibleStaticTextBase::getSelectedText()
585 SolarMutexGuard aGuard;
587 sal_Int32 nStart( getSelectionStart() );
588 sal_Int32 nEnd( getSelectionEnd() );
590 // #104481# Return the empty string for 'no selection'
591 if( nStart < 0 || nEnd < 0 )
592 return OUString();
594 return getTextRange( nStart, nEnd );
597 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart()
599 SolarMutexGuard aGuard;
601 sal_Int32 i, nPos, nParas;
602 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
604 if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 )
605 return nPos;
608 return nPos;
611 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd()
613 SolarMutexGuard aGuard;
615 sal_Int32 i, nPos, nParas;
616 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
618 if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 )
619 return nPos;
622 return nPos;
625 sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
627 SolarMutexGuard aGuard;
629 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
630 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
632 return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex,
633 aEndIndex.nPara, aEndIndex.nIndex );
636 OUString SAL_CALL AccessibleStaticTextBase::getText()
638 SolarMutexGuard aGuard;
640 sal_Int32 i, nParas;
641 OUStringBuffer aRes;
642 for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
643 aRes.append(mpImpl->GetParagraph(i).getText());
645 return aRes.makeStringAndClear();
648 OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
650 SolarMutexGuard aGuard;
652 if( nStartIndex > nEndIndex )
653 std::swap(nStartIndex, nEndIndex);
654 //if startindex equals endindex we will get nothing. So return an empty string directly.
655 if ( nStartIndex == nEndIndex )
657 return OUString();
659 bool bStart = mpImpl->RemoveLineBreakCount( nStartIndex );
660 //if the start index is just at a "\n", we need to begin from the next char
661 if ( bStart )
663 nStartIndex++;
665 //we need to find out whether the previous position of the current endindex is at "\n" or not
666 //if yes we need to mark it and add "\n" at the end of the result
667 sal_Int32 nTemp = nEndIndex - 1;
668 bool bEnd = mpImpl->RemoveLineBreakCount( nTemp );
669 bool bTemp = mpImpl->RemoveLineBreakCount( nEndIndex );
670 //if the below condition is true it indicates an empty paragraph with just a "\n"
671 //so we need to set one "\n" flag to avoid duplication.
672 if ( bStart && bEnd && ( nStartIndex == nEndIndex) )
674 bEnd = false;
676 //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure
677 //the char before "\n" is included. Because string returned by this function will not include
678 //the char at the endindex.
679 if ( bTemp )
681 nEndIndex++;
683 OUStringBuffer aRes;
684 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
685 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
687 // #102170# Special case: start and end paragraph are identical
688 if( aStartIndex.nPara == aEndIndex.nPara )
690 //we don't return the string directly now for that we have to do some further process for "\n"
691 aRes = mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex );
693 else
695 sal_Int32 i( aStartIndex.nPara );
696 aRes = mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex,
697 mpImpl->GetParagraph(i).getCharacterCount()/*-1*/);
698 ++i;
700 // paragraphs inbetween are fully included
701 for( ; i<aEndIndex.nPara; ++i )
703 aRes.append(cNewLine);
704 aRes.append(mpImpl->GetParagraph(i).getText());
707 if( i<=aEndIndex.nPara )
709 //if the below condition is matched it means that endindex is at mid of the last paragraph
710 //we need to add a "\n" before we add the last part of the string.
711 if ( !bEnd && aEndIndex.nIndex )
713 aRes.append(cNewLine);
715 aRes.append(mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex ));
718 //According to the flag we marked before, we have to add "\n" at the beginning
719 //or at the end of the result string.
720 if ( bStart )
722 aRes.insert(0, OUStringChar(cNewLine));
724 if ( bEnd )
726 aRes.append(OUStringChar(cNewLine));
728 return aRes.makeStringAndClear();
731 css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
733 SolarMutexGuard aGuard;
735 bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
736 EPosition aPos( mpImpl->Range2Internal(nIndex) );
738 css::accessibility::TextSegment aResult;
740 if( AccessibleTextType::PARAGRAPH == aTextType )
742 // #106393# Special casing one behind last paragraph is
743 // not necessary, since then, we return the content and
744 // boundary of that last paragraph. Range2Internal is
745 // tolerant against that, and returns the last paragraph
746 // in aPos.nPara.
748 // retrieve full text of the paragraph
749 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
751 // #112814# Adapt the start index with the paragraph offset
752 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
753 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
755 else if ( AccessibleTextType::ATTRIBUTE_RUN == aTextType )
757 SvxAccessibleTextAdapter& rTextForwarder = mpImpl->GetParagraph( aPos.nIndex ).GetTextForwarder();
758 sal_Int32 nStartIndex, nEndIndex;
759 if ( rTextForwarder.GetAttributeRun( nStartIndex, nEndIndex, aPos.nPara, aPos.nIndex, true ) )
761 aResult.SegmentText = getTextRange( nStartIndex, nEndIndex );
762 aResult.SegmentStart = nStartIndex;
763 aResult.SegmentEnd = nEndIndex;
766 else
768 // No special handling required, forward to wrapped class
769 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType );
771 // #112814# Adapt the start index with the paragraph offset
772 mpImpl->CorrectTextSegment( aResult, aPos.nPara );
773 if ( bLineBreak )
775 aResult.SegmentText = OUString(cNewLine);
779 return aResult;
782 css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
784 SolarMutexGuard aGuard;
786 sal_Int32 nOldIdx = nIndex;
787 bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
788 EPosition aPos( mpImpl->Range2Internal(nIndex) );
790 css::accessibility::TextSegment aResult;
792 if( AccessibleTextType::PARAGRAPH == aTextType )
794 if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() )
796 // #103589# Special casing one behind the last paragraph
797 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
799 // #112814# Adapt the start index with the paragraph offset
800 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
802 else if( aPos.nPara > 0 )
804 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText();
806 // #112814# Adapt the start index with the paragraph offset
807 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) );
810 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
812 else
814 // No special handling required, forward to wrapped class
815 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType );
817 // #112814# Adapt the start index with the paragraph offset
818 mpImpl->CorrectTextSegment( aResult, aPos.nPara );
819 if ( bLineBreak && (nOldIdx-1) >= 0)
821 aResult = getTextAtIndex( nOldIdx-1, aTextType );
825 return aResult;
828 css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
830 SolarMutexGuard aGuard;
832 sal_Int32 nTemp = nIndex+1;
833 bool bLineBreak = mpImpl->RemoveLineBreakCount( nTemp );
834 mpImpl->RemoveLineBreakCount( nIndex );
835 EPosition aPos( mpImpl->Range2Internal(nIndex) );
837 css::accessibility::TextSegment aResult;
839 if( AccessibleTextType::PARAGRAPH == aTextType )
841 // Special casing one behind the last paragraph is not
842 // necessary, this case is invalid here for
843 // getTextBehindIndex
844 if( aPos.nPara + 1 < mpImpl->GetParagraphCount() )
846 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText();
848 // #112814# Adapt the start index with the paragraph offset
849 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) );
850 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
853 else
855 // No special handling required, forward to wrapped class
856 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType );
858 // #112814# Adapt the start index with the paragraph offset
859 mpImpl->CorrectTextSegment( aResult, aPos.nPara );
860 if ( bLineBreak )
862 aResult.SegmentText = OUStringChar(cNewLine) + aResult.SegmentText;
866 return aResult;
869 sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
871 SolarMutexGuard aGuard;
873 if( nStartIndex > nEndIndex )
874 std::swap(nStartIndex, nEndIndex);
876 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
877 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
879 return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex,
880 aEndIndex.nPara, aEndIndex.nIndex );
883 // XAccessibleTextAttributes
884 uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< OUString >& RequestedAttributes )
886 // get the intersection of the default attributes of all paragraphs
888 SolarMutexGuard aGuard;
890 PropertyValueVector aDefAttrVec(
891 comphelper::sequenceToContainer<PropertyValueVector>(mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes )) );
893 const sal_Int32 nParaCount = mpImpl->GetParagraphCount();
894 for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara )
896 uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes );
897 PropertyValueVector aIntersectionVec;
899 for ( const auto& rDefAttr : aDefAttrVec )
901 const beans::PropertyValue* pItr = aSeq.getConstArray();
902 const beans::PropertyValue* pEnd = pItr + aSeq.getLength();
903 const beans::PropertyValue* pFind = std::find_if( pItr, pEnd, PropertyValueEqualFunctor(rDefAttr) );
904 if ( pFind != pEnd )
906 aIntersectionVec.push_back( *pFind );
910 aDefAttrVec.swap( aIntersectionVec );
912 if ( aDefAttrVec.empty() )
914 break;
918 return comphelper::containerToSequence(aDefAttrVec);
921 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< OUString >& RequestedAttributes )
923 // get those default attributes of the paragraph, which are not part
924 // of the intersection of all paragraphs and add them to the run attributes
926 SolarMutexGuard aGuard;
928 EPosition aPos( mpImpl->Index2Internal( nIndex ) );
929 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
930 uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes );
931 uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes );
932 uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes );
933 PropertyValueVector aDiffVec;
935 const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray();
936 const sal_Int32 nLength = aDefAttrSeq.getLength();
937 for ( sal_Int32 i = 0; i < nLength; ++i )
939 const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray();
940 const beans::PropertyValue* pEnd = pItr + aIntersectionSeq.getLength();
941 bool bNone = std::none_of( pItr, pEnd, PropertyValueEqualFunctor( pDefAttr[i] ) );
942 if ( bNone && pDefAttr[i].Handle != 0)
944 aDiffVec.push_back( pDefAttr[i] );
948 return ::comphelper::concatSequences( aRunAttrSeq, comphelper::containerToSequence(aDiffVec) );
951 tools::Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const
953 return mpImpl->GetParagraphBoundingBox();
956 } // end of namespace accessibility
959 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */