summaryrefslogtreecommitdiffstats
path: root/editeng/source/accessibility/AccessibleStaticTextBase.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'editeng/source/accessibility/AccessibleStaticTextBase.cxx')
-rw-r--r--editeng/source/accessibility/AccessibleStaticTextBase.cxx967
1 files changed, 967 insertions, 0 deletions
diff --git a/editeng/source/accessibility/AccessibleStaticTextBase.cxx b/editeng/source/accessibility/AccessibleStaticTextBase.cxx
new file mode 100644
index 000000000..1dcf37b8d
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleStaticTextBase.cxx
@@ -0,0 +1,967 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// Global header
+
+
+#include <utility>
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <rtl/ustrbuf.hxx>
+#include <tools/debug.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+
+
+// Project-local header
+
+
+#include <editeng/editdata.hxx>
+#include <editeng/unoedprx.hxx>
+#include <editeng/AccessibleStaticTextBase.hxx>
+#include <editeng/AccessibleEditableTextPara.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+/* TODO:
+ =====
+
+ - separate adapter functionality from AccessibleStaticText class
+
+ - refactor common loops into templates, using mem_fun
+
+ */
+
+namespace accessibility
+{
+ typedef std::vector< beans::PropertyValue > PropertyValueVector;
+
+ namespace {
+
+ class PropertyValueEqualFunctor
+ {
+ const beans::PropertyValue& m_rPValue;
+
+ public:
+ explicit PropertyValueEqualFunctor(const beans::PropertyValue& rPValue)
+ : m_rPValue(rPValue)
+ {}
+ bool operator() ( const beans::PropertyValue& rhs ) const
+ {
+ return ( m_rPValue.Name == rhs.Name && m_rPValue.Value == rhs.Value );
+ }
+ };
+
+ }
+
+ sal_Unicode const cNewLine(0x0a);
+
+
+ // Static Helper
+
+
+ static ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex )
+ {
+ DBG_ASSERT(nStartPara >= 0 &&
+ nStartIndex >= 0 &&
+ nEndPara >= 0 &&
+ nEndIndex >= 0,
+ "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow");
+
+ return ESelection(nStartPara, nStartIndex, nEndPara, nEndIndex);
+ }
+
+
+ // AccessibleStaticTextBase_Impl declaration
+
+
+ /** AccessibleStaticTextBase_Impl
+
+ This class implements the AccessibleStaticTextBase
+ functionality, mainly by forwarding the calls to an aggregated
+ AccessibleEditableTextPara. As this is a therefore non-trivial
+ adapter, factoring out the common functionality from
+ AccessibleEditableTextPara might be a profitable future task.
+ */
+ class AccessibleStaticTextBase_Impl
+ {
+ friend class AccessibleStaticTextBase;
+ public:
+
+ // receive pointer to our frontend class and view window
+ AccessibleStaticTextBase_Impl();
+
+ void SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource );
+
+ void SetEventSource( const uno::Reference< XAccessible >& rInterface )
+ {
+
+ mxThis = rInterface;
+ }
+
+ void SetOffset( const Point& );
+
+ void Dispose();
+
+ AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const;
+ sal_Int32 GetParagraphCount() const;
+
+ EPosition Index2Internal( sal_Int32 nFlatIndex ) const
+ {
+
+ return ImpCalcInternal( nFlatIndex, false );
+ }
+
+ EPosition Range2Internal( sal_Int32 nFlatIndex ) const
+ {
+
+ return ImpCalcInternal( nFlatIndex, true );
+ }
+
+ sal_Int32 Internal2Index( EPosition nEEIndex ) const;
+
+ void CorrectTextSegment( TextSegment& aTextSegment,
+ int nPara ) const;
+
+ bool SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex );
+ bool CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex );
+
+ tools::Rectangle GetParagraphBoundingBox() const;
+ bool RemoveLineBreakCount( sal_Int32& rIndex );
+
+ private:
+
+ EPosition ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const;
+
+ // our frontend class (the one implementing the actual
+ // interface). That's not necessarily the one containing the impl
+ // pointer
+ uno::Reference< XAccessible > mxThis;
+
+ // implements our functionality, we're just an adapter (guarded by solar mutex)
+ mutable rtl::Reference<AccessibleEditableTextPara> mxTextParagraph;
+
+ // a wrapper for the text forwarders (guarded by solar mutex)
+ mutable SvxEditSourceAdapter maEditSource;
+ };
+
+
+ // AccessibleStaticTextBase_Impl implementation
+
+
+ AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl() :
+ mxTextParagraph( new AccessibleEditableTextPara(nullptr) ),
+ maEditSource()
+ {
+
+ // TODO: this is still somewhat of a hack, all the more since
+ // now the maTextParagraph has an empty parent reference set
+ }
+
+ void AccessibleStaticTextBase_Impl::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+
+ maEditSource.SetEditSource( std::move(pEditSource) );
+ if( mxTextParagraph.is() )
+ mxTextParagraph->SetEditSource( &maEditSource );
+ }
+
+ void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint )
+ {
+ if( mxTextParagraph.is() )
+ mxTextParagraph->SetEEOffset( rPoint );
+ }
+
+ void AccessibleStaticTextBase_Impl::Dispose()
+ {
+
+ // we're the owner of the paragraph, so destroy it, too
+ if( mxTextParagraph.is() )
+ mxTextParagraph->Dispose();
+
+ // drop references
+ mxThis = nullptr;
+ mxTextParagraph.clear();
+ }
+
+ AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const
+ {
+
+ if( !mxTextParagraph.is() )
+ throw lang::DisposedException ("object has been already disposed", mxThis );
+
+ // TODO: Have a different method on AccessibleEditableTextPara
+ // that does not care about state changes
+ mxTextParagraph->SetParagraphIndex( nPara );
+
+ return *mxTextParagraph;
+ }
+
+ sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const
+ {
+
+ if( !mxTextParagraph.is() )
+ return 0;
+ else
+ return mxTextParagraph->GetTextForwarder().GetParagraphCount();
+ }
+
+ sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index( EPosition nEEIndex ) const
+ {
+ // XXX checks for overflow and returns maximum if so
+ sal_Int32 aRes(0);
+ for(sal_Int32 i=0; i<nEEIndex.nPara; ++i)
+ {
+ sal_Int32 nCount = GetParagraph(i).getCharacterCount();
+ if (SAL_MAX_INT32 - aRes > nCount)
+ return SAL_MAX_INT32;
+ aRes += nCount;
+ }
+
+ if (SAL_MAX_INT32 - aRes > nEEIndex.nIndex)
+ return SAL_MAX_INT32;
+ return aRes + nEEIndex.nIndex;
+ }
+
+ void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment& aTextSegment,
+ int nPara ) const
+ {
+ // Keep 'invalid' values at the TextSegment
+ if( aTextSegment.SegmentStart != -1 &&
+ aTextSegment.SegmentEnd != -1 )
+ {
+ // #112814# Correct TextSegment by paragraph offset
+ sal_Int32 nOffset(0);
+ int i;
+ for(i=0; i<nPara; ++i)
+ nOffset += GetParagraph(i).getCharacterCount();
+
+ aTextSegment.SegmentStart += nOffset;
+ aTextSegment.SegmentEnd += nOffset;
+ }
+ }
+
+ EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const
+ {
+
+ if( nFlatIndex < 0 )
+ throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
+ mxThis);
+ // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually
+
+ sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount;
+ for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara )
+ {
+ nCurrCount = GetParagraph( nCurrPara ).getCharacterCount();
+ nCurrIndex += nCurrCount;
+ if( nCurrIndex >= nFlatIndex )
+ {
+ // check overflow
+ DBG_ASSERT(nCurrPara >= 0 &&
+ nFlatIndex - nCurrIndex + nCurrCount >= 0,
+ "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
+
+ return EPosition(nCurrPara, nFlatIndex - nCurrIndex + nCurrCount);
+ }
+ }
+
+ // #102170# Allow one-past the end for ranges
+ if( bExclusive && nCurrIndex == nFlatIndex )
+ {
+ // check overflow
+ DBG_ASSERT(nCurrPara > 0 &&
+ nFlatIndex - nCurrIndex + nCurrCount >= 0,
+ "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
+
+ return EPosition(nCurrPara-1, nFlatIndex - nCurrIndex + nCurrCount);
+ }
+
+ // not found? Out of bounds
+ throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
+ mxThis);
+ }
+
+ bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex )
+ {
+
+ if( !mxTextParagraph.is() )
+ return false;
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
+ return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
+ }
+ catch( const uno::RuntimeException& )
+ {
+ return false;
+ }
+ }
+
+ bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex )
+ {
+
+ if( !mxTextParagraph.is() )
+ return false;
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
+ mxTextParagraph->GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+ bool aRetVal;
+
+ // save current selection
+ ESelection aOldSelection;
+
+ rCacheVF.GetSelection( aOldSelection );
+ rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
+ aRetVal = rCacheVF.Copy();
+ rCacheVF.SetSelection( aOldSelection ); // restore
+
+ return aRetVal;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ return false;
+ }
+ }
+
+ tools::Rectangle AccessibleStaticTextBase_Impl::GetParagraphBoundingBox() const
+ {
+ tools::Rectangle aRect;
+ if( mxTextParagraph.is() )
+ {
+ awt::Rectangle aAwtRect = mxTextParagraph->getBounds();
+ aRect = tools::Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) );
+ }
+ else
+ {
+ aRect.SetEmpty();
+ }
+ return aRect;
+ }
+ //the input argument is the index(including "\n" ) in the string.
+ //the function will calculate the actual index(not including "\n") in the string.
+ //and return true if the index is just at a "\n"
+ bool AccessibleStaticTextBase_Impl::RemoveLineBreakCount( sal_Int32& rIndex )
+ {
+ // get the total char number inside the cell.
+ sal_Int32 i, nCount, nParas;
+ for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i )
+ nCount += GetParagraph(i).getCharacterCount();
+ nCount = nCount + (nParas-1);
+ if( nCount == 0 && rIndex == 0) return false;
+
+
+ sal_Int32 nCurrPara, nCurrCount;
+ sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0;
+ sal_Int32 nParaCount = GetParagraphCount();
+ for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ )
+ {
+ nCurrCount += GetParagraph( nCurrPara ).getCharacterCount();
+ nLineBreakPos = nCurrCount++;
+ if ( rIndex == nLineBreakPos )
+ {
+ rIndex -= (++nLineBreakCount);//(++nLineBreakCount);
+ if ( rIndex < 0)
+ {
+ rIndex = 0;
+ }
+ //if the index is at the last position of the last paragraph
+ //there is no "\n" , so we should increase rIndex by 1 and return false.
+ if ( (nCurrPara+1) == nParaCount )
+ {
+ rIndex++;
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else if ( rIndex < nLineBreakPos )
+ {
+ rIndex -= nLineBreakCount;
+ return false;
+ }
+ else
+ {
+ nLineBreakCount++;
+ }
+ }
+ return false;
+ }
+
+
+ // AccessibleStaticTextBase implementation
+
+ AccessibleStaticTextBase::AccessibleStaticTextBase( std::unique_ptr< SvxEditSource > && pEditSource ) :
+ mpImpl( new AccessibleStaticTextBase_Impl() )
+ {
+ SolarMutexGuard aGuard;
+
+ SetEditSource( std::move(pEditSource) );
+ }
+
+ AccessibleStaticTextBase::~AccessibleStaticTextBase()
+ {
+ }
+
+ void AccessibleStaticTextBase::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->SetEditSource( std::move(pEditSource) );
+ }
+
+ void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface )
+ {
+ mpImpl->SetEventSource( rInterface );
+
+ }
+
+ void AccessibleStaticTextBase::SetOffset( const Point& rPoint )
+ {
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->SetOffset( rPoint );
+ }
+
+ void AccessibleStaticTextBase::Dispose()
+ {
+ mpImpl->Dispose();
+
+ }
+
+ // XAccessibleContext
+ sal_Int32 AccessibleStaticTextBase::getAccessibleChildCount()
+ {
+ // no children at all
+ return 0;
+ }
+
+ uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int32 /*i*/ )
+ {
+ // no children at all
+ return uno::Reference< XAccessible >();
+ }
+
+ uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ )
+ {
+ // no children at all
+ return uno::Reference< XAccessible >();
+ }
+
+ // XAccessibleText
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nPos, nParas;
+ for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ {
+ if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 )
+ return nPos;
+ }
+
+ return nPos;
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex )
+ {
+ return setSelection(nIndex, nIndex);
+ }
+
+ sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ EPosition aPos( mpImpl->Index2Internal(nIndex) );
+
+ return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex );
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
+ {
+ SolarMutexGuard aGuard;
+
+ //get the actual index without "\n"
+ mpImpl->RemoveLineBreakCount( nIndex );
+
+ EPosition aPos( mpImpl->Index2Internal(nIndex) );
+
+ return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes );
+ }
+
+ awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ // #108900# Allow ranges for nIndex, as one-past-the-end
+ // values are now legal, too.
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ // #i70916# Text in spread sheet cells return the wrong extents
+ AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
+ awt::Rectangle aParaBounds( rPara.getBounds() );
+ awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) );
+ aBounds.X += aParaBounds.X;
+ aBounds.Y += aParaBounds.Y;
+
+ return aBounds;
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nCount, nParas;
+ for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ nCount += mpImpl->GetParagraph(i).getCharacterCount();
+ //count on the number of "\n" which equals number of paragraphs decrease 1.
+ nCount = nCount + (nParas-1);
+ return nCount;
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint )
+ {
+ SolarMutexGuard aGuard;
+
+ const sal_Int32 nParas( mpImpl->GetParagraphCount() );
+ sal_Int32 nIndex;
+ int i;
+ for( i=0; i<nParas; ++i )
+ {
+ // TODO: maybe exploit the fact that paragraphs are
+ // ordered vertically for early exit
+
+ // #i70916# Text in spread sheet cells return the wrong extents
+ AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i );
+ awt::Rectangle aParaBounds( rPara.getBounds() );
+ awt::Point aPoint( rPoint );
+ aPoint.X -= aParaBounds.X;
+ aPoint.Y -= aParaBounds.Y;
+
+ // #112814# Use correct index offset
+ if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 )
+ return mpImpl->Internal2Index(EPosition(i, nIndex));
+ }
+
+ return -1;
+ }
+
+ OUString SAL_CALL AccessibleStaticTextBase::getSelectedText()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nStart( getSelectionStart() );
+ sal_Int32 nEnd( getSelectionEnd() );
+
+ // #104481# Return the empty string for 'no selection'
+ if( nStart < 0 || nEnd < 0 )
+ return OUString();
+
+ return getTextRange( nStart, nEnd );
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nPos, nParas;
+ for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ {
+ if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 )
+ return nPos;
+ }
+
+ return nPos;
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nPos, nParas;
+ for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ {
+ if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 )
+ return nPos;
+ }
+
+ return nPos;
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
+ EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
+
+ return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex,
+ aEndIndex.nPara, aEndIndex.nIndex );
+ }
+
+ OUString SAL_CALL AccessibleStaticTextBase::getText()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nParas;
+ OUStringBuffer aRes;
+ for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ aRes.append(mpImpl->GetParagraph(i).getText());
+
+ return aRes.makeStringAndClear();
+ }
+
+ OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ if( nStartIndex > nEndIndex )
+ std::swap(nStartIndex, nEndIndex);
+ //if startindex equals endindex we will get nothing. So return an empty string directly.
+ if ( nStartIndex == nEndIndex )
+ {
+ return OUString();
+ }
+ bool bStart = mpImpl->RemoveLineBreakCount( nStartIndex );
+ //if the start index is just at a "\n", we need to begin from the next char
+ if ( bStart )
+ {
+ nStartIndex++;
+ }
+ //we need to find out whether the previous position of the current endindex is at "\n" or not
+ //if yes we need to mark it and add "\n" at the end of the result
+ sal_Int32 nTemp = nEndIndex - 1;
+ bool bEnd = mpImpl->RemoveLineBreakCount( nTemp );
+ bool bTemp = mpImpl->RemoveLineBreakCount( nEndIndex );
+ //if the below condition is true it indicates an empty paragraph with just a "\n"
+ //so we need to set one "\n" flag to avoid duplication.
+ if ( bStart && bEnd && ( nStartIndex == nEndIndex) )
+ {
+ bEnd = false;
+ }
+ //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure
+ //the char before "\n" is included. Because string returned by this function will not include
+ //the char at the endindex.
+ if ( bTemp )
+ {
+ nEndIndex++;
+ }
+ OUStringBuffer aRes;
+ EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
+ EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
+
+ // #102170# Special case: start and end paragraph are identical
+ if( aStartIndex.nPara == aEndIndex.nPara )
+ {
+ //we don't return the string directly now for that we have to do some further process for "\n"
+ aRes = mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex );
+ }
+ else
+ {
+ sal_Int32 i( aStartIndex.nPara );
+ aRes = mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex,
+ mpImpl->GetParagraph(i).getCharacterCount()/*-1*/);
+ ++i;
+
+ // paragraphs inbetween are fully included
+ for( ; i<aEndIndex.nPara; ++i )
+ {
+ aRes.append(cNewLine);
+ aRes.append(mpImpl->GetParagraph(i).getText());
+ }
+
+ if( i<=aEndIndex.nPara )
+ {
+ //if the below condition is matched it means that endindex is at mid of the last paragraph
+ //we need to add a "\n" before we add the last part of the string.
+ if ( !bEnd && aEndIndex.nIndex )
+ {
+ aRes.append(cNewLine);
+ }
+ aRes.append(mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex ));
+ }
+ }
+ //According to the flag we marked before, we have to add "\n" at the beginning
+ //or at the end of the result string.
+ if ( bStart )
+ {
+ aRes.insert(0, OUStringChar(cNewLine));
+ }
+ if ( bEnd )
+ {
+ aRes.append(OUStringChar(cNewLine));
+ }
+ return aRes.makeStringAndClear();
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ css::accessibility::TextSegment aResult;
+
+ if( AccessibleTextType::PARAGRAPH == aTextType )
+ {
+ // #106393# Special casing one behind last paragraph is
+ // not necessary, since then, we return the content and
+ // boundary of that last paragraph. Range2Internal is
+ // tolerant against that, and returns the last paragraph
+ // in aPos.nPara.
+
+ // retrieve full text of the paragraph
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ }
+ else if ( AccessibleTextType::ATTRIBUTE_RUN == aTextType )
+ {
+ SvxAccessibleTextAdapter& rTextForwarder = mpImpl->GetParagraph( aPos.nIndex ).GetTextForwarder();
+ sal_Int32 nStartIndex, nEndIndex;
+ if ( rTextForwarder.GetAttributeRun( nStartIndex, nEndIndex, aPos.nPara, aPos.nIndex, true ) )
+ {
+ aResult.SegmentText = getTextRange( nStartIndex, nEndIndex );
+ aResult.SegmentStart = nStartIndex;
+ aResult.SegmentEnd = nEndIndex;
+ }
+ }
+ else
+ {
+ // No special handling required, forward to wrapped class
+ aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType );
+
+ // #112814# Adapt the start index with the paragraph offset
+ mpImpl->CorrectTextSegment( aResult, aPos.nPara );
+ if ( bLineBreak )
+ {
+ aResult.SegmentText = OUString(cNewLine);
+ }
+ }
+
+ return aResult;
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nOldIdx = nIndex;
+ bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ css::accessibility::TextSegment aResult;
+
+ if( AccessibleTextType::PARAGRAPH == aTextType )
+ {
+ if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() )
+ {
+ // #103589# Special casing one behind the last paragraph
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
+ }
+ else if( aPos.nPara > 0 )
+ {
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) );
+ }
+
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ }
+ else
+ {
+ // No special handling required, forward to wrapped class
+ aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType );
+
+ // #112814# Adapt the start index with the paragraph offset
+ mpImpl->CorrectTextSegment( aResult, aPos.nPara );
+ if ( bLineBreak && (nOldIdx-1) >= 0)
+ {
+ aResult = getTextAtIndex( nOldIdx-1, aTextType );
+ }
+ }
+
+ return aResult;
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nTemp = nIndex+1;
+ bool bLineBreak = mpImpl->RemoveLineBreakCount( nTemp );
+ mpImpl->RemoveLineBreakCount( nIndex );
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ css::accessibility::TextSegment aResult;
+
+ if( AccessibleTextType::PARAGRAPH == aTextType )
+ {
+ // Special casing one behind the last paragraph is not
+ // necessary, this case is invalid here for
+ // getTextBehindIndex
+ if( aPos.nPara + 1 < mpImpl->GetParagraphCount() )
+ {
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) );
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ }
+ }
+ else
+ {
+ // No special handling required, forward to wrapped class
+ aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType );
+
+ // #112814# Adapt the start index with the paragraph offset
+ mpImpl->CorrectTextSegment( aResult, aPos.nPara );
+ if ( bLineBreak )
+ {
+ aResult.SegmentText = OUStringChar(cNewLine) + aResult.SegmentText;
+ }
+ }
+
+ return aResult;
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ if( nStartIndex > nEndIndex )
+ std::swap(nStartIndex, nEndIndex);
+
+ EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
+ EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
+
+ return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex,
+ aEndIndex.nPara, aEndIndex.nIndex );
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
+ {
+ return false;
+ }
+
+ // XAccessibleTextAttributes
+ uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< OUString >& RequestedAttributes )
+ {
+ // get the intersection of the default attributes of all paragraphs
+
+ SolarMutexGuard aGuard;
+
+ PropertyValueVector aDefAttrVec(
+ comphelper::sequenceToContainer<PropertyValueVector>(mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes )) );
+
+ const sal_Int32 nParaCount = mpImpl->GetParagraphCount();
+ for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara )
+ {
+ uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes );
+ PropertyValueVector aIntersectionVec;
+
+ for ( const auto& rDefAttr : aDefAttrVec )
+ {
+ const beans::PropertyValue* pItr = aSeq.getConstArray();
+ const beans::PropertyValue* pEnd = pItr + aSeq.getLength();
+ const beans::PropertyValue* pFind = std::find_if( pItr, pEnd, PropertyValueEqualFunctor(rDefAttr) );
+ if ( pFind != pEnd )
+ {
+ aIntersectionVec.push_back( *pFind );
+ }
+ }
+
+ aDefAttrVec.swap( aIntersectionVec );
+
+ if ( aDefAttrVec.empty() )
+ {
+ break;
+ }
+ }
+
+ return comphelper::containerToSequence(aDefAttrVec);
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< OUString >& RequestedAttributes )
+ {
+ // get those default attributes of the paragraph, which are not part
+ // of the intersection of all paragraphs and add them to the run attributes
+
+ SolarMutexGuard aGuard;
+
+ EPosition aPos( mpImpl->Index2Internal( nIndex ) );
+ AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
+ uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes );
+ uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes );
+ uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes );
+ PropertyValueVector aDiffVec;
+
+ const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray();
+ const sal_Int32 nLength = aDefAttrSeq.getLength();
+ for ( sal_Int32 i = 0; i < nLength; ++i )
+ {
+ const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray();
+ const beans::PropertyValue* pEnd = pItr + aIntersectionSeq.getLength();
+ bool bNone = std::none_of( pItr, pEnd, PropertyValueEqualFunctor( pDefAttr[i] ) );
+ if ( bNone && pDefAttr[i].Handle != 0)
+ {
+ aDiffVec.push_back( pDefAttr[i] );
+ }
+ }
+
+ return ::comphelper::concatSequences( aRunAttrSeq, comphelper::containerToSequence(aDiffVec) );
+ }
+
+ tools::Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const
+ {
+ return mpImpl->GetParagraphBoundingBox();
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */