957 lines
33 KiB
C++
957 lines
33 KiB
C++
/* -*- 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/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 )
|
|
{
|
|
mpThis = rInterface.get();
|
|
}
|
|
|
|
void SetOffset( const Point& );
|
|
|
|
void Dispose();
|
|
|
|
AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const;
|
|
sal_Int32 GetParagraphCount() const;
|
|
|
|
EPaM Index2Internal( sal_Int32 nFlatIndex ) const
|
|
{
|
|
|
|
return ImpCalcInternal( nFlatIndex, false );
|
|
}
|
|
|
|
EPaM Range2Internal( sal_Int32 nFlatIndex ) const
|
|
{
|
|
|
|
return ImpCalcInternal( nFlatIndex, true );
|
|
}
|
|
|
|
sal_Int32 Internal2Index( EPaM 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:
|
|
|
|
EPaM 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. Note that this is not an uno::Reference to prevent ref-counting cycles and leaks.
|
|
XAccessible* mpThis;
|
|
|
|
// 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()
|
|
: mpThis(nullptr)
|
|
, mxTextParagraph(new AccessibleEditableTextPara(nullptr))
|
|
{
|
|
|
|
// 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
|
|
mpThis = nullptr;
|
|
mxTextParagraph.clear();
|
|
}
|
|
|
|
AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const
|
|
{
|
|
|
|
if( !mxTextParagraph.is() )
|
|
throw lang::DisposedException (u"object has been already disposed"_ustr, mpThis );
|
|
|
|
// 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(EPaM 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;
|
|
}
|
|
}
|
|
|
|
EPaM AccessibleStaticTextBase_Impl::ImpCalcInternal(sal_Int32 nFlatIndex, bool bExclusive) const
|
|
{
|
|
|
|
if( nFlatIndex < 0 )
|
|
throw lang::IndexOutOfBoundsException(u"AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds"_ustr,
|
|
mpThis);
|
|
// 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 EPaM(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 EPaM(nCurrPara - 1, nFlatIndex - nCurrIndex + nCurrCount);
|
|
}
|
|
|
|
// not found? Out of bounds
|
|
throw lang::IndexOutOfBoundsException(u"AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds"_ustr,
|
|
mpThis);
|
|
}
|
|
|
|
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_Int64 AccessibleStaticTextBase::getAccessibleChildCount()
|
|
{
|
|
// no children at all
|
|
return 0;
|
|
}
|
|
|
|
uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int64 /*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;
|
|
|
|
EPaM 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 );
|
|
|
|
EPaM 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.
|
|
EPaM 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(EPaM(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;
|
|
|
|
EPaM aStartIndex(mpImpl->Range2Internal(nStartIndex));
|
|
EPaM 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;
|
|
EPaM aStartIndex(mpImpl->Range2Internal(nStartIndex));
|
|
EPaM 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(OUStringChar(cNewLine) + 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 );
|
|
EPaM 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(EPaM(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 );
|
|
EPaM 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(EPaM(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(EPaM(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 );
|
|
EPaM 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(EPaM(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);
|
|
|
|
EPaM aStartIndex(mpImpl->Range2Internal(nStartIndex));
|
|
EPaM 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 )
|
|
{
|
|
auto it = std::find_if(aSeq.begin(), aSeq.end(), PropertyValueEqualFunctor(rDefAttr));
|
|
if (it != aSeq.end())
|
|
aIntersectionVec.push_back(*it);
|
|
}
|
|
|
|
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;
|
|
|
|
EPaM 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;
|
|
|
|
for (auto& defAttr : aDefAttrSeq)
|
|
{
|
|
bool bNone = std::none_of(aIntersectionSeq.begin(), aIntersectionSeq.end(),
|
|
PropertyValueEqualFunctor(defAttr));
|
|
if (bNone && defAttr.Handle != 0)
|
|
{
|
|
aDiffVec.push_back(defAttr);
|
|
}
|
|
}
|
|
|
|
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: */
|