Use COMReference to handle COM pointers in CreateShortcut
[LibreOffice.git] / editeng / source / accessibility / AccessibleStaticTextBase.cxx
blob9dd2a3483a939d215c49fdcf86a87d13cf798446
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 <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 <comphelper/sequence.hxx>
32 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
33 #include <com/sun/star/uno/Reference.hxx>
34 #include <com/sun/star/awt/Point.hpp>
35 #include <com/sun/star/awt/Rectangle.hpp>
36 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
39 // Project-local header
42 #include <editeng/editdata.hxx>
43 #include <editeng/unoedprx.hxx>
44 #include <editeng/AccessibleStaticTextBase.hxx>
45 #include <editeng/AccessibleEditableTextPara.hxx>
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::accessibility;
51 /* TODO:
52 =====
54 - separate adapter functionality from AccessibleStaticText class
56 - refactor common loops into templates, using mem_fun
60 namespace accessibility
62 typedef std::vector< beans::PropertyValue > PropertyValueVector;
64 namespace {
66 class PropertyValueEqualFunctor
68 const beans::PropertyValue& m_rPValue;
70 public:
71 explicit PropertyValueEqualFunctor(const beans::PropertyValue& rPValue)
72 : m_rPValue(rPValue)
74 bool operator() ( const beans::PropertyValue& rhs ) const
76 return ( m_rPValue.Name == rhs.Name && m_rPValue.Value == rhs.Value );
82 sal_Unicode const cNewLine(0x0a);
85 // Static Helper
88 static ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
89 sal_Int32 nEndPara, sal_Int32 nEndIndex )
91 DBG_ASSERT(nStartPara >= 0 &&
92 nStartIndex >= 0 &&
93 nEndPara >= 0 &&
94 nEndIndex >= 0,
95 "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow");
97 return ESelection(nStartPara, nStartIndex, nEndPara, nEndIndex);
101 // AccessibleStaticTextBase_Impl declaration
104 /** AccessibleStaticTextBase_Impl
106 This class implements the AccessibleStaticTextBase
107 functionality, mainly by forwarding the calls to an aggregated
108 AccessibleEditableTextPara. As this is a therefore non-trivial
109 adapter, factoring out the common functionality from
110 AccessibleEditableTextPara might be a profitable future task.
112 class AccessibleStaticTextBase_Impl
114 friend class AccessibleStaticTextBase;
115 public:
117 // receive pointer to our frontend class and view window
118 AccessibleStaticTextBase_Impl();
120 void SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource );
122 void SetEventSource( const uno::Reference< XAccessible >& rInterface )
124 mpThis = rInterface.get();
127 void SetOffset( const Point& );
129 void Dispose();
131 AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const;
132 sal_Int32 GetParagraphCount() const;
134 EPaM Index2Internal( sal_Int32 nFlatIndex ) const
137 return ImpCalcInternal( nFlatIndex, false );
140 EPaM Range2Internal( sal_Int32 nFlatIndex ) const
143 return ImpCalcInternal( nFlatIndex, true );
146 sal_Int32 Internal2Index( EPaM nEEIndex ) const;
148 void CorrectTextSegment( TextSegment& aTextSegment,
149 int nPara ) const;
151 bool SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
152 sal_Int32 nEndPara, sal_Int32 nEndIndex );
153 bool CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
154 sal_Int32 nEndPara, sal_Int32 nEndIndex );
156 tools::Rectangle GetParagraphBoundingBox() const;
157 bool RemoveLineBreakCount( sal_Int32& rIndex );
159 private:
161 EPaM ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const;
163 // our frontend class (the one implementing the actual
164 // interface). That's not necessarily the one containing the impl
165 // pointer. Note that this is not an uno::Reference to prevent ref-counting cycles and leaks.
166 XAccessible* mpThis;
168 // implements our functionality, we're just an adapter (guarded by solar mutex)
169 mutable rtl::Reference<AccessibleEditableTextPara> mxTextParagraph;
171 // a wrapper for the text forwarders (guarded by solar mutex)
172 mutable SvxEditSourceAdapter maEditSource;
176 // AccessibleStaticTextBase_Impl implementation
179 AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl()
180 : mpThis(nullptr)
181 , mxTextParagraph(new AccessibleEditableTextPara(nullptr))
184 // TODO: this is still somewhat of a hack, all the more since
185 // now the maTextParagraph has an empty parent reference set
188 void AccessibleStaticTextBase_Impl::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
191 maEditSource.SetEditSource( std::move(pEditSource) );
192 if( mxTextParagraph.is() )
193 mxTextParagraph->SetEditSource( &maEditSource );
196 void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint )
198 if( mxTextParagraph.is() )
199 mxTextParagraph->SetEEOffset( rPoint );
202 void AccessibleStaticTextBase_Impl::Dispose()
205 // we're the owner of the paragraph, so destroy it, too
206 if( mxTextParagraph.is() )
207 mxTextParagraph->Dispose();
209 // drop references
210 mpThis = nullptr;
211 mxTextParagraph.clear();
214 AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const
217 if( !mxTextParagraph.is() )
218 throw lang::DisposedException (u"object has been already disposed"_ustr, mpThis );
220 // TODO: Have a different method on AccessibleEditableTextPara
221 // that does not care about state changes
222 mxTextParagraph->SetParagraphIndex( nPara );
224 return *mxTextParagraph;
227 sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const
230 if( !mxTextParagraph.is() )
231 return 0;
232 else
233 return mxTextParagraph->GetTextForwarder().GetParagraphCount();
236 sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index(EPaM nEEIndex) const
238 // XXX checks for overflow and returns maximum if so
239 sal_Int32 aRes(0);
240 for(sal_Int32 i=0; i<nEEIndex.nPara; ++i)
242 sal_Int32 nCount = GetParagraph(i).getCharacterCount();
243 if (SAL_MAX_INT32 - aRes > nCount)
244 return SAL_MAX_INT32;
245 aRes += nCount;
248 if (SAL_MAX_INT32 - aRes > nEEIndex.nIndex)
249 return SAL_MAX_INT32;
250 return aRes + nEEIndex.nIndex;
253 void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment& aTextSegment,
254 int nPara ) const
256 // Keep 'invalid' values at the TextSegment
257 if( aTextSegment.SegmentStart != -1 &&
258 aTextSegment.SegmentEnd != -1 )
260 // #112814# Correct TextSegment by paragraph offset
261 sal_Int32 nOffset(0);
262 int i;
263 for(i=0; i<nPara; ++i)
264 nOffset += GetParagraph(i).getCharacterCount();
266 aTextSegment.SegmentStart += nOffset;
267 aTextSegment.SegmentEnd += nOffset;
271 EPaM AccessibleStaticTextBase_Impl::ImpCalcInternal(sal_Int32 nFlatIndex, bool bExclusive) const
274 if( nFlatIndex < 0 )
275 throw lang::IndexOutOfBoundsException(u"AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds"_ustr,
276 mpThis);
277 // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually
279 sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount;
280 for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara )
282 nCurrCount = GetParagraph( nCurrPara ).getCharacterCount();
283 nCurrIndex += nCurrCount;
284 if( nCurrIndex >= nFlatIndex )
286 // check overflow
287 DBG_ASSERT(nCurrPara >= 0 &&
288 nFlatIndex - nCurrIndex + nCurrCount >= 0,
289 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
291 return EPaM(nCurrPara, nFlatIndex - nCurrIndex + nCurrCount);
295 // #102170# Allow one-past the end for ranges
296 if( bExclusive && nCurrIndex == nFlatIndex )
298 // check overflow
299 DBG_ASSERT(nCurrPara > 0 &&
300 nFlatIndex - nCurrIndex + nCurrCount >= 0,
301 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
303 return EPaM(nCurrPara - 1, nFlatIndex - nCurrIndex + nCurrCount);
306 // not found? Out of bounds
307 throw lang::IndexOutOfBoundsException(u"AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds"_ustr,
308 mpThis);
311 bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
312 sal_Int32 nEndPara, sal_Int32 nEndIndex )
315 if( !mxTextParagraph.is() )
316 return false;
320 SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
321 return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
323 catch( const uno::RuntimeException& )
325 return false;
329 bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
330 sal_Int32 nEndPara, sal_Int32 nEndIndex )
333 if( !mxTextParagraph.is() )
334 return false;
338 SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
339 mxTextParagraph->GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
340 bool aRetVal;
342 // save current selection
343 ESelection aOldSelection;
345 rCacheVF.GetSelection( aOldSelection );
346 rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
347 aRetVal = rCacheVF.Copy();
348 rCacheVF.SetSelection( aOldSelection ); // restore
350 return aRetVal;
352 catch( const uno::RuntimeException& )
354 return false;
358 tools::Rectangle AccessibleStaticTextBase_Impl::GetParagraphBoundingBox() const
360 tools::Rectangle aRect;
361 if( mxTextParagraph.is() )
363 awt::Rectangle aAwtRect = mxTextParagraph->getBounds();
364 aRect = tools::Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) );
366 else
368 aRect.SetEmpty();
370 return aRect;
372 //the input argument is the index(including "\n" ) in the string.
373 //the function will calculate the actual index(not including "\n") in the string.
374 //and return true if the index is just at a "\n"
375 bool AccessibleStaticTextBase_Impl::RemoveLineBreakCount( sal_Int32& rIndex )
377 // get the total char number inside the cell.
378 sal_Int32 i, nCount, nParas;
379 for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i )
380 nCount += GetParagraph(i).getCharacterCount();
381 nCount = nCount + (nParas-1);
382 if( nCount == 0 && rIndex == 0) return false;
385 sal_Int32 nCurrPara, nCurrCount;
386 sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0;
387 sal_Int32 nParaCount = GetParagraphCount();
388 for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ )
390 nCurrCount += GetParagraph( nCurrPara ).getCharacterCount();
391 nLineBreakPos = nCurrCount++;
392 if ( rIndex == nLineBreakPos )
394 rIndex -= (++nLineBreakCount);//(++nLineBreakCount);
395 if ( rIndex < 0)
397 rIndex = 0;
399 //if the index is at the last position of the last paragraph
400 //there is no "\n" , so we should increase rIndex by 1 and return false.
401 if ( (nCurrPara+1) == nParaCount )
403 rIndex++;
404 return false;
406 else
408 return true;
411 else if ( rIndex < nLineBreakPos )
413 rIndex -= nLineBreakCount;
414 return false;
416 else
418 nLineBreakCount++;
421 return false;
425 // AccessibleStaticTextBase implementation
427 AccessibleStaticTextBase::AccessibleStaticTextBase( std::unique_ptr< SvxEditSource > && pEditSource ) :
428 mpImpl( new AccessibleStaticTextBase_Impl() )
430 SolarMutexGuard aGuard;
432 SetEditSource( std::move(pEditSource) );
435 AccessibleStaticTextBase::~AccessibleStaticTextBase()
439 void AccessibleStaticTextBase::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
441 // precondition: solar mutex locked
442 DBG_TESTSOLARMUTEX();
444 mpImpl->SetEditSource( std::move(pEditSource) );
447 void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface )
449 mpImpl->SetEventSource( rInterface );
453 void AccessibleStaticTextBase::SetOffset( const Point& rPoint )
455 // precondition: solar mutex locked
456 DBG_TESTSOLARMUTEX();
458 mpImpl->SetOffset( rPoint );
461 void AccessibleStaticTextBase::Dispose()
463 mpImpl->Dispose();
467 // XAccessibleContext
468 sal_Int64 AccessibleStaticTextBase::getAccessibleChildCount()
470 // no children at all
471 return 0;
474 uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int64 /*i*/ )
476 // no children at all
477 return uno::Reference< XAccessible >();
480 uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ )
482 // no children at all
483 return uno::Reference< XAccessible >();
486 // XAccessibleText
487 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition()
489 SolarMutexGuard aGuard;
491 sal_Int32 i, nPos, nParas;
492 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
494 if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 )
495 return nPos;
498 return nPos;
501 sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex )
503 return setSelection(nIndex, nIndex);
506 sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex )
508 SolarMutexGuard aGuard;
510 EPaM aPos(mpImpl->Index2Internal(nIndex));
512 return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex );
515 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
517 SolarMutexGuard aGuard;
519 //get the actual index without "\n"
520 mpImpl->RemoveLineBreakCount( nIndex );
522 EPaM aPos(mpImpl->Index2Internal(nIndex));
524 return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes );
527 awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex )
529 SolarMutexGuard aGuard;
531 // #108900# Allow ranges for nIndex, as one-past-the-end
532 // values are now legal, too.
533 EPaM aPos(mpImpl->Range2Internal(nIndex));
535 // #i70916# Text in spread sheet cells return the wrong extents
536 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
537 awt::Rectangle aParaBounds( rPara.getBounds() );
538 awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) );
539 aBounds.X += aParaBounds.X;
540 aBounds.Y += aParaBounds.Y;
542 return aBounds;
545 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount()
547 SolarMutexGuard aGuard;
549 sal_Int32 i, nCount, nParas;
550 for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
551 nCount += mpImpl->GetParagraph(i).getCharacterCount();
552 //count on the number of "\n" which equals number of paragraphs decrease 1.
553 nCount = nCount + (nParas-1);
554 return nCount;
557 sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint )
559 SolarMutexGuard aGuard;
561 const sal_Int32 nParas( mpImpl->GetParagraphCount() );
562 sal_Int32 nIndex;
563 int i;
564 for( i=0; i<nParas; ++i )
566 // TODO: maybe exploit the fact that paragraphs are
567 // ordered vertically for early exit
569 // #i70916# Text in spread sheet cells return the wrong extents
570 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i );
571 awt::Rectangle aParaBounds( rPara.getBounds() );
572 awt::Point aPoint( rPoint );
573 aPoint.X -= aParaBounds.X;
574 aPoint.Y -= aParaBounds.Y;
576 // #112814# Use correct index offset
577 if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 )
578 return mpImpl->Internal2Index(EPaM(i, nIndex));
581 return -1;
584 OUString SAL_CALL AccessibleStaticTextBase::getSelectedText()
586 SolarMutexGuard aGuard;
588 sal_Int32 nStart( getSelectionStart() );
589 sal_Int32 nEnd( getSelectionEnd() );
591 // #104481# Return the empty string for 'no selection'
592 if( nStart < 0 || nEnd < 0 )
593 return OUString();
595 return getTextRange( nStart, nEnd );
598 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart()
600 SolarMutexGuard aGuard;
602 sal_Int32 i, nPos, nParas;
603 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
605 if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 )
606 return nPos;
609 return nPos;
612 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd()
614 SolarMutexGuard aGuard;
616 sal_Int32 i, nPos, nParas;
617 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
619 if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 )
620 return nPos;
623 return nPos;
626 sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
628 SolarMutexGuard aGuard;
630 EPaM aStartIndex(mpImpl->Range2Internal(nStartIndex));
631 EPaM aEndIndex(mpImpl->Range2Internal(nEndIndex));
633 return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex,
634 aEndIndex.nPara, aEndIndex.nIndex );
637 OUString SAL_CALL AccessibleStaticTextBase::getText()
639 SolarMutexGuard aGuard;
641 sal_Int32 i, nParas;
642 OUStringBuffer aRes;
643 for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
644 aRes.append(mpImpl->GetParagraph(i).getText());
646 return aRes.makeStringAndClear();
649 OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
651 SolarMutexGuard aGuard;
653 if( nStartIndex > nEndIndex )
654 std::swap(nStartIndex, nEndIndex);
655 //if startindex equals endindex we will get nothing. So return an empty string directly.
656 if ( nStartIndex == nEndIndex )
658 return OUString();
660 bool bStart = mpImpl->RemoveLineBreakCount( nStartIndex );
661 //if the start index is just at a "\n", we need to begin from the next char
662 if ( bStart )
664 nStartIndex++;
666 //we need to find out whether the previous position of the current endindex is at "\n" or not
667 //if yes we need to mark it and add "\n" at the end of the result
668 sal_Int32 nTemp = nEndIndex - 1;
669 bool bEnd = mpImpl->RemoveLineBreakCount( nTemp );
670 bool bTemp = mpImpl->RemoveLineBreakCount( nEndIndex );
671 //if the below condition is true it indicates an empty paragraph with just a "\n"
672 //so we need to set one "\n" flag to avoid duplication.
673 if ( bStart && bEnd && ( nStartIndex == nEndIndex) )
675 bEnd = false;
677 //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure
678 //the char before "\n" is included. Because string returned by this function will not include
679 //the char at the endindex.
680 if ( bTemp )
682 nEndIndex++;
684 OUStringBuffer aRes;
685 EPaM aStartIndex(mpImpl->Range2Internal(nStartIndex));
686 EPaM aEndIndex(mpImpl->Range2Internal(nEndIndex));
688 // #102170# Special case: start and end paragraph are identical
689 if( aStartIndex.nPara == aEndIndex.nPara )
691 //we don't return the string directly now for that we have to do some further process for "\n"
692 aRes = mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex );
694 else
696 sal_Int32 i( aStartIndex.nPara );
697 aRes = mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex,
698 mpImpl->GetParagraph(i).getCharacterCount()/*-1*/);
699 ++i;
701 // paragraphs inbetween are fully included
702 for( ; i<aEndIndex.nPara; ++i )
704 aRes.append(OUStringChar(cNewLine) + 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 EPaM 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(EPaM(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 EPaM 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(EPaM(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(EPaM(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 EPaM 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(EPaM(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 EPaM aStartIndex(mpImpl->Range2Internal(nStartIndex));
877 EPaM aEndIndex(mpImpl->Range2Internal(nEndIndex));
879 return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex,
880 aEndIndex.nPara, aEndIndex.nIndex );
883 sal_Bool SAL_CALL AccessibleStaticTextBase::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
885 return false;
888 // XAccessibleTextAttributes
889 uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< OUString >& RequestedAttributes )
891 // get the intersection of the default attributes of all paragraphs
893 SolarMutexGuard aGuard;
895 PropertyValueVector aDefAttrVec(
896 comphelper::sequenceToContainer<PropertyValueVector>(mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes )) );
898 const sal_Int32 nParaCount = mpImpl->GetParagraphCount();
899 for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara )
901 uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes );
902 PropertyValueVector aIntersectionVec;
904 for ( const auto& rDefAttr : aDefAttrVec )
906 auto it = std::find_if(aSeq.begin(), aSeq.end(), PropertyValueEqualFunctor(rDefAttr));
907 if (it != aSeq.end())
908 aIntersectionVec.push_back(*it);
911 aDefAttrVec.swap( aIntersectionVec );
913 if ( aDefAttrVec.empty() )
915 break;
919 return comphelper::containerToSequence(aDefAttrVec);
922 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< OUString >& RequestedAttributes )
924 // get those default attributes of the paragraph, which are not part
925 // of the intersection of all paragraphs and add them to the run attributes
927 SolarMutexGuard aGuard;
929 EPaM aPos(mpImpl->Index2Internal(nIndex));
930 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
931 uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes );
932 uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes );
933 uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes );
934 PropertyValueVector aDiffVec;
936 for (auto& defAttr : aDefAttrSeq)
938 bool bNone = std::none_of(aIntersectionSeq.begin(), aIntersectionSeq.end(),
939 PropertyValueEqualFunctor(defAttr));
940 if (bNone && defAttr.Handle != 0)
942 aDiffVec.push_back(defAttr);
946 return comphelper::concatSequences(aRunAttrSeq, aDiffVec);
949 tools::Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const
951 return mpImpl->GetParagraphBoundingBox();
954 } // end of namespace accessibility
957 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */