/* -*- 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 .
 */
 
#include <vcl/svapp.hxx>
#include <com/sun/star/style/LineSpacing.hpp>
#include <com/sun/star/text/ControlCharacter.hpp>
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/text/TextRangeSelection.hpp>
 
#include <svl/itemset.hxx>
#include <svl/itempool.hxx>
#include <svl/intitem.hxx>
#include <svl/eitem.hxx>
#include <rtl/instance.hxx>
 
#include <editeng/fontitem.hxx>
#include <editeng/tstpitem.hxx>
#include <editeng/unoprnms.hxx>
#include <editeng/unotext.hxx>
#include <editeng/unoedsrc.hxx>
#include <editeng/unonrule.hxx>
#include <editeng/unofdesc.hxx>
#include <editeng/unofield.hxx>
#include <editeng/flditem.hxx>
#include <editeng/numitem.hxx>
#include <editeng/editeng.hxx>
#include <editeng/outliner.hxx>
#include <editeng/unoipset.hxx>
#include <comphelper/servicehelper.hxx>
#include <comphelper/serviceinfohelper.hxx>
#include <cppuhelper/supportsservice.hxx>
 
#include <editeng/unonames.hxx>
 
#include <memory>
 
using namespace ::cppu;
using namespace ::com::sun::star;
 
namespace {
 
ESelection toESelection(const text::TextRangeSelection& rSel)
{
    ESelection aESel;
    aESel.nStartPara = rSel.Start.Paragraph;
    aESel.nStartPos = rSel.Start.PositionInParagraph;
    aESel.nEndPara = rSel.End.Paragraph;
    aESel.nEndPos = rSel.End.PositionInParagraph;
    return aESel;
}
 
}
 
#define QUERYINT( xint ) \
    if( rType == cppu::UnoType<xint>::get() ) \
        return uno::makeAny(uno::Reference< xint >(this))
 
const SvxItemPropertySet* ImplGetSvxUnoOutlinerTextCursorSvxPropertySet()
{
    static SvxItemPropertySet aTextCursorSvxPropertySet( ImplGetSvxUnoOutlinerTextCursorPropertyMap(), EditEngine::GetGlobalItemPool() );
    return &aTextCursorSvxPropertySet;
}
 
const SfxItemPropertyMapEntry* ImplGetSvxTextPortionPropertyMap()
{
    // Propertymap for an Outliner Text
    static const SfxItemPropertyMapEntry aSvxTextPortionPropertyMap[] =
    {
        SVX_UNOEDIT_CHAR_PROPERTIES,
        SVX_UNOEDIT_FONT_PROPERTIES,
        SVX_UNOEDIT_OUTLINER_PROPERTIES,
        SVX_UNOEDIT_PARA_PROPERTIES,
        { OUString("TextField"),                     EE_FEATURE_FIELD,   cppu::UnoType<text::XTextField>::get(),   beans::PropertyAttribute::READONLY, 0 },
        { OUString("TextPortionType"),               WID_PORTIONTYPE,    ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
        { OUString("TextUserDefinedAttributes"),     EE_CHAR_XMLATTRIBS,     cppu::UnoType<css::container::XNameContainer>::get(),        0,     0},
        { OUString("ParaUserDefinedAttributes"),     EE_PARA_XMLATTRIBS,     cppu::UnoType<css::container::XNameContainer>::get(),        0,     0},
        { OUString(), 0, css::uno::Type(), 0, 0 }
    };
    return aSvxTextPortionPropertyMap;
}
const SvxItemPropertySet* ImplGetSvxTextPortionSvxPropertySet()
{
    static SvxItemPropertySet aSvxTextPortionPropertySet( ImplGetSvxTextPortionPropertyMap(), EditEngine::GetGlobalItemPool() );
    return &aSvxTextPortionPropertySet;
}
 
const SfxItemPropertySet* ImplGetSvxTextPortionSfxPropertySet()
{
    static SfxItemPropertySet aSvxTextPortionSfxPropertySet( ImplGetSvxTextPortionPropertyMap() );
    return &aSvxTextPortionSfxPropertySet;
}
 
const SfxItemPropertyMapEntry* ImplGetSvxUnoOutlinerTextCursorPropertyMap()
{
    // Propertymap for an Outliner Text
    static const SfxItemPropertyMapEntry aSvxUnoOutlinerTextCursorPropertyMap[] =
    {
        SVX_UNOEDIT_CHAR_PROPERTIES,
        SVX_UNOEDIT_FONT_PROPERTIES,
        SVX_UNOEDIT_OUTLINER_PROPERTIES,
        SVX_UNOEDIT_PARA_PROPERTIES,
        { OUString("TextUserDefinedAttributes"),         EE_CHAR_XMLATTRIBS,     cppu::UnoType<css::container::XNameContainer>::get(),        0,     0},
        { OUString("ParaUserDefinedAttributes"),         EE_PARA_XMLATTRIBS,     cppu::UnoType<css::container::XNameContainer>::get(),        0,     0},
        { OUString(), 0, css::uno::Type(), 0, 0 }
    };
 
    return aSvxUnoOutlinerTextCursorPropertyMap;
}
const SfxItemPropertySet* ImplGetSvxUnoOutlinerTextCursorSfxPropertySet()
{
    static SfxItemPropertySet aTextCursorSfxPropertySet( ImplGetSvxUnoOutlinerTextCursorPropertyMap() );
    return &aTextCursorSfxPropertySet;
}
 
 
// helper for Item/Property conversion
 
 
void GetSelection( struct ESelection& rSel, SvxTextForwarder const * pForwarder ) throw()
{
    DBG_ASSERT( pForwarder, "I need a valid SvxTextForwarder!" );
    if( pForwarder )
    {
        sal_Int32 nParaCount = pForwarder->GetParagraphCount();
        if(nParaCount>0)
            nParaCount--;
 
        rSel = ESelection( 0,0, nParaCount, pForwarder->GetTextLen( nParaCount ));
    }
}
 
void CheckSelection( struct ESelection& rSel, SvxTextForwarder const * pForwarder ) throw()
{
    DBG_ASSERT( pForwarder, "I need a valid SvxTextForwarder!" );
    if( pForwarder )
    {
        if( rSel.nStartPara == EE_PARA_MAX_COUNT )
        {
            ::GetSelection( rSel, pForwarder );
        }
        else
        {
            ESelection aMaxSelection;
            GetSelection( aMaxSelection, pForwarder );
 
            // check start position
            if( rSel.nStartPara < aMaxSelection.nStartPara )
            {
                rSel.nStartPara = aMaxSelection.nStartPara;
                rSel.nStartPos = aMaxSelection.nStartPos;
            }
            else if( rSel.nStartPara > aMaxSelection.nEndPara )
            {
                rSel.nStartPara = aMaxSelection.nEndPara;
                rSel.nStartPos = aMaxSelection.nEndPos;
            }
            else if( rSel.nStartPos  > pForwarder->GetTextLen( rSel.nStartPara ) )
            {
                rSel.nStartPos = pForwarder->GetTextLen( rSel.nStartPara );
            }
 
            // check end position
            if( rSel.nEndPara < aMaxSelection.nStartPara )
            {
                rSel.nEndPara = aMaxSelection.nStartPara;
                rSel.nEndPos = aMaxSelection.nStartPos;
            }
            else if( rSel.nEndPara > aMaxSelection.nEndPara )
            {
                rSel.nEndPara = aMaxSelection.nEndPara;
                rSel.nEndPos = aMaxSelection.nEndPos;
            }
            else if( rSel.nEndPos > pForwarder->GetTextLen( rSel.nEndPara ) )
            {
                rSel.nEndPos = pForwarder->GetTextLen( rSel.nEndPara );
            }
        }
    }
}
 
void CheckSelection( struct ESelection& rSel, SvxEditSource *pEdit ) throw()
{
    if (!pEdit)
        return;
    CheckSelection( rSel, pEdit->GetTextForwarder() );
}
 
 
// class SvxUnoTextRangeBase
 
 
UNO3_GETIMPLEMENTATION_IMPL( SvxUnoTextRangeBase );
 
SvxUnoTextRangeBase::SvxUnoTextRangeBase(const SvxItemPropertySet* _pSet)
    : mpPropSet(_pSet)
{
}
 
SvxUnoTextRangeBase::SvxUnoTextRangeBase(const SvxEditSource* pSource, const SvxItemPropertySet* _pSet)
: mpPropSet(_pSet)
{
    SolarMutexGuard aGuard;
 
    DBG_ASSERT(pSource,"SvxUnoTextRangeBase: I need a valid SvxEditSource!");
 
    mpEditSource = pSource->Clone();
    if (mpEditSource != nullptr)
    {
        ESelection aSelection;
        ::GetSelection( aSelection, mpEditSource->GetTextForwarder() );
        SetSelection( aSelection );
 
        mpEditSource->addRange( this );
    }
}
 
SvxUnoTextRangeBase::SvxUnoTextRangeBase(const SvxUnoTextRangeBase& rRange)
:   text::XTextRange()
,   beans::XPropertySet()
,   beans::XMultiPropertySet()
,   beans::XMultiPropertyStates()
,   beans::XPropertyState()
,   lang::XServiceInfo()
,   text::XTextRangeCompare()
,   lang::XUnoTunnel()
,   osl::DebugBase<SvxUnoTextRangeBase>()
,   mpPropSet(rRange.getPropertySet())
{
    SolarMutexGuard aGuard;
 
    if (rRange.mpEditSource)
        mpEditSource = rRange.mpEditSource->Clone();
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        maSelection  = rRange.maSelection;
        CheckSelection( maSelection, pForwarder );
    }
 
    if( mpEditSource )
        mpEditSource->addRange( this );
}
 
SvxUnoTextRangeBase::~SvxUnoTextRangeBase() throw()
{
    if( mpEditSource )
        mpEditSource->removeRange( this );
}
 
void SvxUnoTextRangeBase::SetEditSource( SvxEditSource* pSource ) throw()
{
    DBG_ASSERT(pSource,"SvxUnoTextRangeBase: I need a valid SvxEditSource!");
    DBG_ASSERT(mpEditSource==nullptr,"SvxUnoTextRangeBase::SetEditSource called while SvxEditSource already set" );
 
    mpEditSource.reset( pSource );
 
    maSelection.nStartPara = EE_PARA_MAX_COUNT;
 
    if( mpEditSource )
        mpEditSource->addRange( this );
}
 
/** puts a field item with a copy of the given FieldData into the itemset
    corresponding with this range */
void SvxUnoTextRangeBase::attachField( std::unique_ptr<SvxFieldData> pData ) throw()
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        SvxFieldItem aField( std::move(pData), EE_FEATURE_FIELD );
        pForwarder->QuickInsertField( std::move(aField), maSelection );
    }
}
 
void SvxUnoTextRangeBase::SetSelection( const ESelection& rSelection ) throw()
{
    SolarMutexGuard aGuard;
 
    maSelection = rSelection;
    CheckSelection( maSelection, mpEditSource.get() );
}
 
// Interface XTextRange ( XText )
 
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextRangeBase::getStart()
{
    SolarMutexGuard aGuard;
 
    uno::Reference< text::XTextRange > xRange;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        CheckSelection( maSelection, pForwarder );
 
        SvxUnoTextBase* pText = SvxUnoTextBase::getImplementation( getText() );
 
        if(pText == nullptr)
            throw uno::RuntimeException();
 
        SvxUnoTextRange* pRange = new SvxUnoTextRange( *pText );
        xRange = pRange;
 
        ESelection aNewSel = maSelection;
        aNewSel.nEndPara = aNewSel.nStartPara;
        aNewSel.nEndPos  = aNewSel.nStartPos;
        pRange->SetSelection( aNewSel );
    }
 
    return xRange;
}
 
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextRangeBase::getEnd()
{
    SolarMutexGuard aGuard;
 
    uno::Reference< text::XTextRange > xRet;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        CheckSelection( maSelection, pForwarder );
 
        SvxUnoTextBase* pText = SvxUnoTextBase::getImplementation( getText() );
 
        if(pText == nullptr)
            throw uno::RuntimeException();
 
        SvxUnoTextRange* pNew = new SvxUnoTextRange( *pText );
        xRet = pNew;
 
        ESelection aNewSel = maSelection;
        aNewSel.nStartPara = aNewSel.nEndPara;
        aNewSel.nStartPos  = aNewSel.nEndPos;
        pNew->SetSelection( aNewSel );
    }
    return xRet;
}
 
OUString SAL_CALL SvxUnoTextRangeBase::getString()
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        CheckSelection( maSelection, pForwarder );
 
        return pForwarder->GetText( maSelection );
    }
    else
    {
        return OUString();
    }
}
 
void SAL_CALL SvxUnoTextRangeBase::setString(const OUString& aString)
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        CheckSelection( maSelection, pForwarder );
 
        OUString aConverted(convertLineEnd(aString, LINEEND_LF));  // Simply count the number of line endings
 
        pForwarder->QuickInsertText( aConverted, maSelection );
        mpEditSource->UpdateData();
 
        //  Adapt selection
        //! It would be easier if the EditEngine would return the selection
        //! on QuickInsertText...
        CollapseToStart();
 
        sal_Int32 nLen = aConverted.getLength();
        if (nLen)
            GoRight( nLen, true );
    }
}
 
// Interface beans::XPropertySet
uno::Reference< beans::XPropertySetInfo > SAL_CALL SvxUnoTextRangeBase::getPropertySetInfo()
{
    return mpPropSet->getPropertySetInfo();
}
 
void SAL_CALL SvxUnoTextRangeBase::setPropertyValue(const OUString& PropertyName, const uno::Any& aValue)
{
    if (PropertyName == UNO_TR_PROP_SELECTION)
    {
        text::TextRangeSelection aSel = aValue.get<text::TextRangeSelection>();
        SetSelection(toESelection(aSel));
 
        return;
    }
 
    _setPropertyValue( PropertyName, aValue );
}
 
void SvxUnoTextRangeBase::_setPropertyValue( const OUString& PropertyName, const uno::Any& aValue, sal_Int32 nPara )
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        CheckSelection( maSelection, pForwarder );
 
        const SfxItemPropertySimpleEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName );
        if ( pMap )
        {
            ESelection aSel( GetSelection() );
            bool bParaAttrib = (pMap->nWID >= EE_PARA_START) && ( pMap->nWID <= EE_PARA_END );
 
            if( nPara == -1 && !bParaAttrib )
            {
                SfxItemSet aOldSet( pForwarder->GetAttribs( aSel ) );
                // we have a selection and no para attribute
                SfxItemSet aNewSet( *aOldSet.GetPool(), aOldSet.GetRanges() );
 
                setPropertyValue( pMap, aValue, maSelection, aOldSet, aNewSet );
 
 
                pForwarder->QuickSetAttribs( aNewSet, GetSelection() );
            }
            else
            {
                sal_Int32 nEndPara;
 
                if( nPara == -1 )
                {
                    nPara = aSel.nStartPara;
                    nEndPara = aSel.nEndPara;
                }
                else
                {
                    // only one paragraph
                    nEndPara = nPara;
                }
 
                while( nPara <= nEndPara )
                {
                    // we have a paragraph
                    SfxItemSet aSet( pForwarder->GetParaAttribs( nPara ) );
                    setPropertyValue( pMap, aValue, maSelection, aSet, aSet );
                    pForwarder->SetParaAttribs( nPara, aSet );
                    nPara++;
                }
            }
 
            GetEditSource()->UpdateData();
            return;
        }
    }
 
    throw beans::UnknownPropertyException();
}
 
void SvxUnoTextRangeBase::setPropertyValue( const SfxItemPropertySimpleEntry* pMap, const uno::Any& rValue, const ESelection& rSelection, const SfxItemSet& rOldSet, SfxItemSet& rNewSet )
{
    if(!SetPropertyValueHelper( pMap, rValue, rNewSet, &rSelection, GetEditSource() ))
    {
        // For parts of composite items with multiple properties (eg background)
        // must be taken from the document before the old item.
        rNewSet.Put(rOldSet.Get(pMap->nWID));  // Old Item in new Set
        SvxItemPropertySet::setPropertyValue(pMap, rValue, rNewSet, false );
    }
}
 
bool SvxUnoTextRangeBase::SetPropertyValueHelper( const SfxItemPropertySimpleEntry* pMap, const uno::Any& aValue, SfxItemSet& rNewSet, const ESelection* pSelection /* = NULL */, SvxEditSource* pEditSource /* = NULL*/ )
{
    switch( pMap->nWID )
    {
    case WID_FONTDESC:
        {
            awt::FontDescriptor aDesc;
            if(aValue >>= aDesc)
            {
                SvxUnoFontDescriptor::FillItemSet( aDesc, rNewSet );
                return true;
            }
        }
        break;
 
    case EE_PARA_NUMBULLET:
        {
            uno::Reference< container::XIndexReplace > xRule;
            return !aValue.hasValue() || ((aValue >>= xRule) && !xRule.is());
        }
 
    case WID_NUMLEVEL:
        {
            SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
            if(pForwarder && pSelection)
            {
                sal_Int16 nLevel = sal_Int16();
                if( aValue >>= nLevel )
                {
                    // #101004# Call interface method instead of unsafe cast
                    if(! pForwarder->SetDepth( pSelection->nStartPara, nLevel ) )
                        throw lang::IllegalArgumentException();
 
                    return true;
                }
            }
        }
        break;
    case WID_NUMBERINGSTARTVALUE:
        {
            SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
            if(pForwarder && pSelection)
            {
                sal_Int16 nStartValue = -1;
                if( aValue >>= nStartValue )
                {
                    pForwarder->SetNumberingStartValue( pSelection->nStartPara, nStartValue );
                    return true;
                }
            }
        }
        break;
    case WID_PARAISNUMBERINGRESTART:
        {
            SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
            if(pForwarder && pSelection)
            {
                bool bParaIsNumberingRestart = false;
                if( aValue >>= bParaIsNumberingRestart )
                {
                    pForwarder->SetParaIsNumberingRestart( pSelection->nStartPara, bParaIsNumberingRestart );
                    return true;
                }
            }
        }
        break;
    case EE_PARA_BULLETSTATE:
        {
            bool bBullet = true;
            if( aValue >>= bBullet )
            {
                SfxBoolItem aItem( EE_PARA_BULLETSTATE, bBullet );
                rNewSet.Put(aItem);
                return true;
            }
        }
        break;
 
    default:
        return false;
    }
 
    throw lang::IllegalArgumentException();
}
 
uno::Any SAL_CALL SvxUnoTextRangeBase::getPropertyValue(const OUString& PropertyName)
{
    if (PropertyName == UNO_TR_PROP_SELECTION)
    {
        const ESelection& rSel = GetSelection();
        text::TextRangeSelection aSel;
        aSel.Start.Paragraph = rSel.nStartPara;
        aSel.Start.PositionInParagraph = rSel.nStartPos;
        aSel.End.Paragraph = rSel.nEndPara;
        aSel.End.PositionInParagraph = rSel.nEndPos;
        return uno::makeAny(aSel);
    }
 
    return _getPropertyValue( PropertyName );
}
 
uno::Any SvxUnoTextRangeBase::_getPropertyValue(const OUString& PropertyName, sal_Int32 nPara )
{
    SolarMutexGuard aGuard;
 
    uno::Any aAny;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        const SfxItemPropertySimpleEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName );
        if( pMap )
        {
            std::unique_ptr<SfxItemSet> pAttribs;
            if( nPara != -1 )
                pAttribs = pForwarder->GetParaAttribs( nPara ).Clone();
            else
                pAttribs = pForwarder->GetAttribs( GetSelection() ).Clone();
 
            //  Replace Dontcare with Default, so that one always has a mirror
            pAttribs->ClearInvalidItems();
 
            getPropertyValue( pMap, aAny, *pAttribs );
 
            return aAny;
        }
    }
 
    throw beans::UnknownPropertyException();
}
 
void SvxUnoTextRangeBase::getPropertyValue( const SfxItemPropertySimpleEntry* pMap, uno::Any& rAny, const SfxItemSet& rSet )
{
    switch( pMap->nWID )
    {
    case EE_FEATURE_FIELD:
        if ( rSet.GetItemState( EE_FEATURE_FIELD, false ) == SfxItemState::SET )
        {
            const SvxFieldItem* pItem = rSet.GetItem<SvxFieldItem>( EE_FEATURE_FIELD );
            const SvxFieldData* pData = pItem->GetField();
            uno::Reference< text::XTextRange > xAnchor( this );
 
            // get presentation string for field
            boost::optional<Color> pTColor;
            boost::optional<Color> pFColor;
 
            SvxTextForwarder* pForwarder = mpEditSource->GetTextForwarder();
            OUString aPresentation( pForwarder->CalcFieldValue( SvxFieldItem(*pData, EE_FEATURE_FIELD), maSelection.nStartPara, maSelection.nStartPos, pTColor, pFColor ) );
 
            uno::Reference< text::XTextField > xField( new SvxUnoTextField( xAnchor, aPresentation, pData ) );
            rAny <<= xField;
        }
        break;
 
    case WID_PORTIONTYPE:
        if ( rSet.GetItemState( EE_FEATURE_FIELD, false ) == SfxItemState::SET )
        {
            rAny <<= OUString("TextField");
        }
        else
        {
            rAny <<= OUString("Text");
        }
        break;
 
    default:
        if(!GetPropertyValueHelper( *const_cast<SfxItemSet*>(&rSet), pMap, rAny, &maSelection, GetEditSource() ))
            rAny = SvxItemPropertySet::getPropertyValue(pMap, rSet, true, false );
    }
}
 
bool SvxUnoTextRangeBase::GetPropertyValueHelper(  SfxItemSet const & rSet, const SfxItemPropertySimpleEntry* pMap, uno::Any& aAny, const ESelection* pSelection /* = NULL */, SvxEditSource* pEditSource /* = NULL */ )
{
    switch( pMap->nWID )
    {
    case WID_FONTDESC:
        {
            awt::FontDescriptor aDesc;
            SvxUnoFontDescriptor::FillFromItemSet( rSet, aDesc );
            aAny <<= aDesc;
        }
        break;
 
    case EE_PARA_NUMBULLET:
        {
            SfxItemState eState = rSet.GetItemState( EE_PARA_NUMBULLET );
            if( eState != SfxItemState::SET && eState != SfxItemState::DEFAULT)
                throw uno::RuntimeException();
 
            const SvxNumBulletItem* pBulletItem = rSet.GetItem( EE_PARA_NUMBULLET );
 
            if( pBulletItem == nullptr )
                throw uno::RuntimeException();
 
            aAny <<= SvxCreateNumRule( pBulletItem->GetNumRule() );
        }
        break;
 
    case WID_NUMLEVEL:
        {
            SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
            if(pForwarder && pSelection)
            {
                sal_Int16 nLevel = pForwarder->GetDepth( pSelection->nStartPara );
                if( nLevel >= 0 )
                    aAny <<= nLevel;
            }
        }
        break;
    case WID_NUMBERINGSTARTVALUE:
        {
            SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
            if(pForwarder && pSelection)
                aAny <<= pForwarder->GetNumberingStartValue( pSelection->nStartPara );
        }
        break;
    case WID_PARAISNUMBERINGRESTART:
        {
            SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
            if(pForwarder && pSelection)
                aAny <<= pForwarder->IsParaIsNumberingRestart( pSelection->nStartPara );
        }
        break;
 
    case EE_PARA_BULLETSTATE:
        {
            bool bState = false;
            SfxItemState eState = rSet.GetItemState( EE_PARA_BULLETSTATE );
            if( eState == SfxItemState::SET || eState == SfxItemState::DEFAULT )
            {
                const SfxBoolItem* pItem = rSet.GetItem<SfxBoolItem>( EE_PARA_BULLETSTATE );
                bState = pItem->GetValue();
            }
 
            aAny <<= bState;
        }
        break;
    default:
 
        return false;
    }
 
    return true;
}
 
// is not (yet) supported
void SAL_CALL SvxUnoTextRangeBase::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {}
void SAL_CALL SvxUnoTextRangeBase::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {}
void SAL_CALL SvxUnoTextRangeBase::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {}
void SAL_CALL SvxUnoTextRangeBase::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {}
 
// XMultiPropertySet
void SAL_CALL SvxUnoTextRangeBase::setPropertyValues( const uno::Sequence< OUString >& aPropertyNames, const uno::Sequence< uno::Any >& aValues )
{
    _setPropertyValues( aPropertyNames, aValues );
}
 
void SvxUnoTextRangeBase::_setPropertyValues( const uno::Sequence< OUString >& aPropertyNames, const uno::Sequence< uno::Any >& aValues, sal_Int32 nPara )
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        CheckSelection( maSelection, pForwarder );
 
        ESelection aSel( GetSelection() );
 
        const OUString* pPropertyNames = aPropertyNames.getConstArray();
        const uno::Any* pValues = aValues.getConstArray();
        sal_Int32 nCount = aPropertyNames.getLength();
 
        sal_Int32 nEndPara = nPara;
        sal_Int32 nTempPara = nPara;
 
        if( nTempPara == -1 )
        {
            nTempPara = aSel.nStartPara;
            nEndPara = aSel.nEndPara;
        }
 
        SfxItemSet* pOldAttrSet = nullptr;
        SfxItemSet* pNewAttrSet = nullptr;
 
        SfxItemSet* pOldParaSet = nullptr;
        SfxItemSet* pNewParaSet = nullptr;
 
        for( ; nCount; nCount--, pPropertyNames++, pValues++ )
        {
            const SfxItemPropertySimpleEntry* pMap = mpPropSet->getPropertyMapEntry( *pPropertyNames );
 
            if( pMap )
            {
                bool bParaAttrib = (pMap->nWID >= EE_PARA_START) && ( pMap->nWID <= EE_PARA_END );
 
                if( (nPara == -1) && !bParaAttrib )
                {
                    if( nullptr == pNewAttrSet )
                    {
                        const SfxItemSet aSet( pForwarder->GetAttribs( aSel ) );
                        pOldAttrSet = new SfxItemSet( aSet );
                        pNewAttrSet = new SfxItemSet( *pOldAttrSet->GetPool(), pOldAttrSet->GetRanges() );
                    }
 
                    setPropertyValue( pMap, *pValues, GetSelection(), *pOldAttrSet, *pNewAttrSet );
 
                    if( pMap->nWID >= EE_ITEMS_START && pMap->nWID <= EE_ITEMS_END )
                    {
                        const SfxPoolItem* pItem;
                        if( pNewAttrSet->GetItemState( pMap->nWID, true, &pItem ) == SfxItemState::SET )
                        {
                            pOldAttrSet->Put( *pItem );
                        }
                    }
                }
                else
                {
                    if( nullptr == pNewParaSet )
                    {
                        const SfxItemSet aSet( pForwarder->GetParaAttribs( nTempPara ) );
                        pOldParaSet = new SfxItemSet( aSet );
                        pNewParaSet = new SfxItemSet( *pOldParaSet->GetPool(), pOldParaSet->GetRanges() );
                    }
 
                    setPropertyValue( pMap, *pValues, GetSelection(), *pOldParaSet, *pNewParaSet );
 
                    if( pMap->nWID >= EE_ITEMS_START && pMap->nWID <= EE_ITEMS_END )
                    {
                        const SfxPoolItem* pItem;
                        if( pNewParaSet->GetItemState( pMap->nWID, true, &pItem ) == SfxItemState::SET )
                        {
                            pOldParaSet->Put( *pItem );
                        }
                    }
 
                }
            }
        }
 
        bool bNeedsUpdate = false;
 
        if( pNewParaSet )
        {
            if( pNewParaSet->Count() )
            {
                while( nTempPara <= nEndPara )
                {
                    SfxItemSet aSet( pForwarder->GetParaAttribs( nTempPara ) );
                    aSet.Put( *pNewParaSet );
                    pForwarder->SetParaAttribs( nTempPara, aSet );
                    nTempPara++;
                }
                bNeedsUpdate = true;
            }
 
            delete pNewParaSet;
            delete pOldParaSet;
        }
 
        if( pNewAttrSet )
        {
            if( pNewAttrSet->Count() )
            {
                pForwarder->QuickSetAttribs( *pNewAttrSet, GetSelection() );
                bNeedsUpdate = true;
            }
            delete pNewAttrSet;
            delete pOldAttrSet;
 
        }
 
        if( bNeedsUpdate )
            GetEditSource()->UpdateData();
    }
}
 
uno::Sequence< uno::Any > SAL_CALL SvxUnoTextRangeBase::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
{
    return _getPropertyValues( aPropertyNames );
}
 
uno::Sequence< uno::Any > SvxUnoTextRangeBase::_getPropertyValues( const uno::Sequence< OUString >& aPropertyNames, sal_Int32 nPara )
{
    SolarMutexGuard aGuard;
 
    sal_Int32 nCount = aPropertyNames.getLength();
 
 
    uno::Sequence< uno::Any > aValues( nCount );
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        std::unique_ptr<SfxItemSet> pAttribs;
        if( nPara != -1 )
            pAttribs = pForwarder->GetParaAttribs( nPara ).Clone();
        else
            pAttribs = pForwarder->GetAttribs( GetSelection() ).Clone();
 
        pAttribs->ClearInvalidItems();
 
        const OUString* pPropertyNames = aPropertyNames.getConstArray();
        uno::Any* pValues = aValues.getArray();
 
        for( ; nCount; nCount--, pPropertyNames++, pValues++ )
        {
            const SfxItemPropertySimpleEntry* pMap = mpPropSet->getPropertyMapEntry( *pPropertyNames );
            if( pMap )
            {
                getPropertyValue( pMap, *pValues, *pAttribs );
            }
        }
    }
 
    return aValues;
}
 
void SAL_CALL SvxUnoTextRangeBase::addPropertiesChangeListener( const uno::Sequence< OUString >& , const uno::Reference< beans::XPropertiesChangeListener >& )
{
}
 
void SAL_CALL SvxUnoTextRangeBase::removePropertiesChangeListener( const uno::Reference< beans::XPropertiesChangeListener >& )
{
}
 
void SAL_CALL SvxUnoTextRangeBase::firePropertiesChangeEvent( const uno::Sequence< OUString >& , const uno::Reference< beans::XPropertiesChangeListener >& )
{
}
 
// beans::XPropertyState
beans::PropertyState SAL_CALL SvxUnoTextRangeBase::getPropertyState( const OUString& PropertyName )
{
    return _getPropertyState( PropertyName );
}
 
static const sal_uInt16 aSvxUnoFontDescriptorWhichMap[] = { EE_CHAR_FONTINFO, EE_CHAR_FONTHEIGHT, EE_CHAR_ITALIC,
                                                  EE_CHAR_UNDERLINE, EE_CHAR_WEIGHT, EE_CHAR_STRIKEOUT, EE_CHAR_CASEMAP,
                                                  EE_CHAR_WLM, 0 };
 
beans::PropertyState SvxUnoTextRangeBase::_getPropertyState(const SfxItemPropertySimpleEntry* pMap, sal_Int32 nPara)
{
    if ( pMap )
    {
        SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
        if( pForwarder )
        {
            SfxItemState eItemState = SfxItemState::UNKNOWN;
            sal_uInt16 nWID = 0;
 
            switch( pMap->nWID )
            {
            case WID_FONTDESC:
                {
                    const sal_uInt16* pWhichId = aSvxUnoFontDescriptorWhichMap;
                    SfxItemState eTempItemState = SfxItemState::UNKNOWN;
                    while( *pWhichId )
                    {
                        if(nPara != -1)
                            eTempItemState = pForwarder->GetItemState( nPara, *pWhichId );
                        else
                            eTempItemState = pForwarder->GetItemState( GetSelection(), *pWhichId );
 
                        switch( eTempItemState )
                        {
                        case SfxItemState::DISABLED:
                        case SfxItemState::DONTCARE:
                            eItemState = SfxItemState::DONTCARE;
                            break;
 
                        case SfxItemState::DEFAULT:
                            if( eItemState != SfxItemState::DEFAULT )
                            {
                                if( eItemState == SfxItemState::UNKNOWN )
                                    eItemState = SfxItemState::DEFAULT;
                            }
                            break;
 
                        case SfxItemState::READONLY:
                        case SfxItemState::SET:
                            if( eItemState != SfxItemState::SET )
                            {
                                if( eItemState == SfxItemState::UNKNOWN )
                                    eItemState = SfxItemState::SET;
                            }
                            break;
                        default:
                            throw beans::UnknownPropertyException();
                        }
 
                        pWhichId++;
                    }
                }
                break;
 
            case WID_NUMLEVEL:
            case WID_NUMBERINGSTARTVALUE:
            case WID_PARAISNUMBERINGRESTART:
                eItemState = SfxItemState::SET;
                break;
 
            default:
                nWID = pMap->nWID;
            }
 
            if( nWID != 0 )
            {
                if( nPara != -1 )
                    eItemState = pForwarder->GetItemState( nPara, nWID );
                else
                    eItemState = pForwarder->GetItemState( GetSelection(), nWID );
            }
 
            switch( eItemState )
            {
            case SfxItemState::DONTCARE:
            case SfxItemState::DISABLED:
                return beans::PropertyState_AMBIGUOUS_VALUE;
            case SfxItemState::READONLY:
            case SfxItemState::SET:
                return beans::PropertyState_DIRECT_VALUE;
            case SfxItemState::DEFAULT:
                return beans::PropertyState_DEFAULT_VALUE;
            default: break;
//              case SfxItemState::UNKNOWN:
            }
        }
    }
    throw beans::UnknownPropertyException();
}
 
beans::PropertyState SvxUnoTextRangeBase::_getPropertyState(const OUString& PropertyName, sal_Int32 nPara /* = -1 */)
{
    SolarMutexGuard aGuard;
 
    return _getPropertyState( mpPropSet->getPropertyMapEntry( PropertyName ), nPara);
}
 
uno::Sequence< beans::PropertyState > SAL_CALL SvxUnoTextRangeBase::getPropertyStates( const uno::Sequence< OUString >& aPropertyName )
{
    return _getPropertyStates( aPropertyName );
}
 
uno::Sequence< beans::PropertyState > SvxUnoTextRangeBase::_getPropertyStates(const uno::Sequence< OUString >& PropertyName, sal_Int32 nPara /* = -1 */)
{
    uno::Sequence< beans::PropertyState > aRet( PropertyName.getLength() );
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        std::unique_ptr<SfxItemSet> pSet;
        if( nPara != -1 )
        {
            pSet.reset(new SfxItemSet( pForwarder->GetParaAttribs( nPara ) ));
        }
        else
        {
            ESelection aSel( GetSelection() );
            CheckSelection( aSel, pForwarder );
            pSet.reset(new SfxItemSet( pForwarder->GetAttribs( aSel, EditEngineAttribs::OnlyHard ) ));
        }
 
        beans::PropertyState* pState = aRet.getArray();
        for( const OUString& rName : PropertyName )
        {
            const SfxItemPropertySimpleEntry* pMap = mpPropSet->getPropertyMapEntry( rName );
            if( !_getOnePropertyStates(pSet.get(), pMap, *pState++) )
            {
                throw beans::UnknownPropertyException();
            }
        }
    }
 
    return aRet;
}
 
bool SvxUnoTextRangeBase::_getOnePropertyStates(const SfxItemSet* pSet, const SfxItemPropertySimpleEntry* pMap, beans::PropertyState& rState)
{
    bool bUnknownPropertyFound = false;
    if(pSet && pMap)
    {
        SfxItemState eItemState = SfxItemState::UNKNOWN;
        sal_uInt16 nWID = 0;
 
        switch( pMap->nWID )
        {
            case WID_FONTDESC:
                {
                    const sal_uInt16* pWhichId = aSvxUnoFontDescriptorWhichMap;
                    SfxItemState eTempItemState = SfxItemState::UNKNOWN;
                    while( *pWhichId )
                    {
                        eTempItemState = pSet->GetItemState( *pWhichId );
 
                        switch( eTempItemState )
                        {
                        case SfxItemState::DISABLED:
                        case SfxItemState::DONTCARE:
                            eItemState = SfxItemState::DONTCARE;
                            break;
 
                        case SfxItemState::DEFAULT:
                            if( eItemState != SfxItemState::DEFAULT )
                            {
                                if( eItemState == SfxItemState::UNKNOWN )
                                    eItemState = SfxItemState::DEFAULT;
                            }
                            break;
 
                        case SfxItemState::READONLY:
                        case SfxItemState::SET:
                            if( eItemState != SfxItemState::SET )
                            {
                                if( eItemState == SfxItemState::UNKNOWN )
                                    eItemState = SfxItemState::SET;
                            }
                            break;
                        default:
                            bUnknownPropertyFound = true;
                            break;
                        }
 
                        pWhichId++;
                    }
                }
                break;
 
            case WID_NUMLEVEL:
            case WID_NUMBERINGSTARTVALUE:
            case WID_PARAISNUMBERINGRESTART:
                eItemState = SfxItemState::SET;
                break;
 
            default:
                nWID = pMap->nWID;
        }
 
        if( bUnknownPropertyFound )
            return !bUnknownPropertyFound;
 
        if( nWID != 0 )
            eItemState = pSet->GetItemState( nWID, false );
 
        switch( eItemState )
        {
                case SfxItemState::READONLY:
                case SfxItemState::SET:
                    rState = beans::PropertyState_DIRECT_VALUE;
                    break;
                case SfxItemState::DEFAULT:
                    rState = beans::PropertyState_DEFAULT_VALUE;
                    break;
//                  case SfxItemState::UNKNOWN:
//                  case SfxItemState::DONTCARE:
//                  case SfxItemState::DISABLED:
                default:
                    rState = beans::PropertyState_AMBIGUOUS_VALUE;
        }
    }
    return !bUnknownPropertyFound;
}
 
void SAL_CALL SvxUnoTextRangeBase::setPropertyToDefault( const OUString& PropertyName )
{
    _setPropertyToDefault( PropertyName );
}
 
void SvxUnoTextRangeBase::_setPropertyToDefault(const OUString& PropertyName, sal_Int32 nPara /* = -1 */)
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
 
    if( pForwarder )
    {
        const SfxItemPropertySimpleEntry* pMap = mpPropSet->getPropertyMapEntry( PropertyName );
        if ( pMap )
        {
            CheckSelection( maSelection, mpEditSource->GetTextForwarder() );
            _setPropertyToDefault( pForwarder, pMap, nPara );
            return;
        }
    }
 
    throw beans::UnknownPropertyException();
}
 
void SvxUnoTextRangeBase::_setPropertyToDefault(SvxTextForwarder* pForwarder, const SfxItemPropertySimpleEntry* pMap, sal_Int32 nPara )
{
    do
    {
        SfxItemSet aSet(*pForwarder->GetPool());
 
        if( pMap->nWID == WID_FONTDESC )
        {
            SvxUnoFontDescriptor::setPropertyToDefault( aSet );
        }
        else if( pMap->nWID == WID_NUMLEVEL )
        {
            // #101004# Call interface method instead of unsafe cast
            pForwarder->SetDepth( maSelection.nStartPara, -1 );
            return;
        }
        else if( pMap->nWID == WID_NUMBERINGSTARTVALUE )
        {
            pForwarder->SetNumberingStartValue( maSelection.nStartPara, -1 );
        }
        else if( pMap->nWID == WID_PARAISNUMBERINGRESTART )
        {
            pForwarder->SetParaIsNumberingRestart( maSelection.nStartPara, false );
        }
        else
        {
            aSet.InvalidateItem( pMap->nWID );
        }
 
        if(nPara != -1)
            pForwarder->SetParaAttribs( nPara, aSet );
        else
            pForwarder->QuickSetAttribs( aSet, GetSelection() );
 
        GetEditSource()->UpdateData();
 
        return;
    }
    while(false);
}
 
uno::Any SAL_CALL SvxUnoTextRangeBase::getPropertyDefault( const OUString& aPropertyName )
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        const SfxItemPropertySimpleEntry* pMap = mpPropSet->getPropertyMapEntry( aPropertyName );
        if( pMap )
        {
            SfxItemPool* pPool = pForwarder->GetPool();
 
            switch( pMap->nWID )
            {
            case WID_FONTDESC:
                return SvxUnoFontDescriptor::getPropertyDefault( pPool );
 
            case WID_NUMLEVEL:
                {
                    uno::Any aAny;
                    return aAny;
                }
 
            case WID_NUMBERINGSTARTVALUE:
                return uno::Any( sal_Int16(-1) );
 
            case WID_PARAISNUMBERINGRESTART:
                return uno::Any( false );
 
            default:
                {
                    // Get Default from ItemPool
                    if(SfxItemPool::IsWhich(pMap->nWID))
                    {
                        SfxItemSet aSet( *pPool,    {{pMap->nWID, pMap->nWID}});
                        aSet.Put(pPool->GetDefaultItem(pMap->nWID));
                        return SvxItemPropertySet::getPropertyValue(pMap, aSet, true, false );
                    }
                }
            }
        }
    }
    throw beans::UnknownPropertyException();
}
 
// beans::XMultiPropertyStates
void SAL_CALL SvxUnoTextRangeBase::setAllPropertiesToDefault()
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
 
    if( pForwarder )
    {
        for (auto & entry : mpPropSet->getPropertyMap().getPropertyEntries())
        {
            _setPropertyToDefault( pForwarder, &entry, -1 );
        }
    }
}
 
void SAL_CALL SvxUnoTextRangeBase::setPropertiesToDefault( const uno::Sequence< OUString >& aPropertyNames )
{
    for( const OUString& rName : aPropertyNames )
    {
        setPropertyToDefault( rName );
    }
}
 
uno::Sequence< uno::Any > SAL_CALL SvxUnoTextRangeBase::getPropertyDefaults( const uno::Sequence< OUString >& aPropertyNames )
{
    uno::Sequence< uno::Any > ret( aPropertyNames.getLength() );
    uno::Any* pDefaults = ret.getArray();
 
    for( const OUString& rName : aPropertyNames )
    {
        *pDefaults++ = getPropertyDefault( rName );
    }
 
    return ret;
}
 
// internal
void SvxUnoTextRangeBase::CollapseToStart() throw()
{
    CheckSelection( maSelection, mpEditSource.get() );
 
    maSelection.nEndPara = maSelection.nStartPara;
    maSelection.nEndPos  = maSelection.nStartPos;
}
 
void SvxUnoTextRangeBase::CollapseToEnd() throw()
{
    CheckSelection( maSelection, mpEditSource.get() );
 
    maSelection.nStartPara = maSelection.nEndPara;
    maSelection.nStartPos  = maSelection.nEndPos;
}
 
bool SvxUnoTextRangeBase::IsCollapsed() throw()
{
    CheckSelection( maSelection, mpEditSource.get() );
 
    return ( maSelection.nStartPara == maSelection.nEndPara &&
             maSelection.nStartPos  == maSelection.nEndPos );
}
 
bool SvxUnoTextRangeBase::GoLeft(sal_Int16 nCount, bool Expand) throw()
{
    CheckSelection( maSelection, mpEditSource.get() );
 
    //  #75098# use end position, as in Writer (start is anchor, end is cursor)
    sal_uInt16 nNewPos = maSelection.nEndPos;
    sal_Int32  nNewPar = maSelection.nEndPara;
 
    bool bOk = true;
    SvxTextForwarder* pForwarder = nullptr;
    while ( nCount > nNewPos && bOk )
    {
        if ( nNewPar == 0 )
            bOk = false;
        else
        {
            if ( !pForwarder )
                pForwarder = mpEditSource->GetTextForwarder();  // first here, it is necessary...
 
            --nNewPar;
            nCount -= nNewPos + 1;
            nNewPos = pForwarder->GetTextLen( nNewPar );
        }
    }
 
    if ( bOk )
    {
        nNewPos = nNewPos - nCount;
        maSelection.nStartPara = nNewPar;
        maSelection.nStartPos  = nNewPos;
    }
 
    if (!Expand)
        CollapseToStart();
 
    return bOk;
}
 
bool SvxUnoTextRangeBase::GoRight(sal_Int16 nCount, bool Expand)  throw()
{
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
        CheckSelection( maSelection, pForwarder );
 
        sal_Int32 nNewPos = maSelection.nEndPos + nCount; //! Overflow???
        sal_Int32  nNewPar = maSelection.nEndPara;
 
        bool bOk = true;
        sal_Int32 nParCount = pForwarder->GetParagraphCount();
        sal_Int32 nThisLen = pForwarder->GetTextLen( nNewPar );
        while ( nNewPos > nThisLen && bOk )
        {
            if ( nNewPar + 1 >= nParCount )
                bOk = false;
            else
            {
                nNewPos -= nThisLen+1;
                ++nNewPar;
                nThisLen = pForwarder->GetTextLen( nNewPar );
            }
        }
 
        if (bOk)
        {
            maSelection.nEndPara = nNewPar;
            maSelection.nEndPos  = nNewPos;
        }
 
        if (!Expand)
            CollapseToEnd();
 
        return bOk;
    }
    return false;
}
 
void SvxUnoTextRangeBase::GotoStart(bool Expand) throw()
{
    maSelection.nStartPara = 0;
    maSelection.nStartPos  = 0;
 
    if (!Expand)
        CollapseToStart();
}
 
void SvxUnoTextRangeBase::GotoEnd(bool Expand) throw()
{
    CheckSelection( maSelection, mpEditSource.get() );
 
    SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
    if( pForwarder )
    {
 
        sal_Int32 nPar = pForwarder->GetParagraphCount();
        if (nPar)
            --nPar;
 
        maSelection.nEndPara = nPar;
        maSelection.nEndPos  = pForwarder->GetTextLen( nPar );
 
        if (!Expand)
            CollapseToEnd();
    }
}
 
// lang::XServiceInfo
sal_Bool SAL_CALL SvxUnoTextRangeBase::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService( this, ServiceName );
}
 
uno::Sequence< OUString > SAL_CALL SvxUnoTextRangeBase::getSupportedServiceNames()
{
    return getSupportedServiceNames_Static();
}
 
uno::Sequence< OUString > SvxUnoTextRangeBase::getSupportedServiceNames_Static()
{
    uno::Sequence< OUString > aSeq(3);
    aSeq[0] = "com.sun.star.style.CharacterProperties";
    aSeq[1] = "com.sun.star.style.CharacterPropertiesComplex";
    aSeq[2] = "com.sun.star.style.CharacterPropertiesAsian";
    return aSeq;
}
 
// XTextRangeCompare
sal_Int16 SAL_CALL SvxUnoTextRangeBase::compareRegionStarts( const uno::Reference< text::XTextRange >& xR1, const uno::Reference< text::XTextRange >& xR2 )
{
    SvxUnoTextRangeBase* pR1 = SvxUnoTextRangeBase::getImplementation( xR1 );
    SvxUnoTextRangeBase* pR2 = SvxUnoTextRangeBase::getImplementation( xR2 );
 
    if( (pR1 == nullptr) || (pR2 == nullptr) )
        throw lang::IllegalArgumentException();
 
    const ESelection& r1 = pR1->maSelection;
    const ESelection& r2 = pR2->maSelection;
 
    if( r1.nStartPara == r2.nStartPara )
    {
        if( r1.nStartPos == r2.nStartPos )
            return 0;
        else
            return r1.nStartPos < r2.nStartPos ? 1 : -1;
    }
    else
    {
        return r1.nStartPara < r2.nStartPara ? 1 : -1;
    }
}
 
sal_Int16 SAL_CALL SvxUnoTextRangeBase::compareRegionEnds( const uno::Reference< text::XTextRange >& xR1, const uno::Reference< text::XTextRange >& xR2 )
{
    SvxUnoTextRangeBase* pR1 = SvxUnoTextRangeBase::getImplementation( xR1 );
    SvxUnoTextRangeBase* pR2 = SvxUnoTextRangeBase::getImplementation( xR2 );
 
    if( (pR1 == nullptr) || (pR2 == nullptr) )
        throw lang::IllegalArgumentException();
 
    const ESelection& r1 = pR1->maSelection;
    const ESelection& r2 = pR2->maSelection;
 
    if( r1.nEndPara == r2.nEndPara )
    {
        if( r1.nEndPos == r2.nEndPos )
            return 0;
        else
            return r1.nEndPos < r2.nEndPos ? 1 : -1;
    }
    else
    {
        return r1.nEndPara < r2.nEndPara ? 1 : -1;
    }
}
 
SvxUnoTextRange::SvxUnoTextRange(const SvxUnoTextBase& rParent, bool bPortion /* = false */)
:SvxUnoTextRangeBase( rParent.GetEditSource(), bPortion ? ImplGetSvxTextPortionSvxPropertySet() : rParent.getPropertySet() ),
 mbPortion( bPortion )
{
    xParentText =  static_cast<text::XText*>(const_cast<SvxUnoTextBase *>(&rParent));
}
 
SvxUnoTextRange::~SvxUnoTextRange() throw()
{
}
 
uno::Any SAL_CALL SvxUnoTextRange::queryAggregation( const uno::Type & rType )
{
    QUERYINT( text::XTextRange );
    else if( rType == cppu::UnoType<beans::XMultiPropertyStates>::get())
        return uno::makeAny(uno::Reference< beans::XMultiPropertyStates >(this));
    else if( rType == cppu::UnoType<beans::XPropertySet>::get())
        return uno::makeAny(uno::Reference< beans::XPropertySet >(this));
    else QUERYINT( beans::XPropertyState );
    else QUERYINT( text::XTextRangeCompare );
    else if( rType == cppu::UnoType<beans::XMultiPropertySet>::get())
        return uno::makeAny(uno::Reference< beans::XMultiPropertySet >(this));
    else QUERYINT( lang::XServiceInfo );
    else QUERYINT( lang::XTypeProvider );
    else QUERYINT( lang::XUnoTunnel );
    else
        return OWeakAggObject::queryAggregation( rType );
}
 
uno::Any SAL_CALL SvxUnoTextRange::queryInterface( const uno::Type & rType )
{
    return OWeakAggObject::queryInterface(rType);
}
 
void SAL_CALL SvxUnoTextRange::acquire()
    throw( )
{
    OWeakAggObject::acquire();
}
 
void SAL_CALL SvxUnoTextRange::release()
    throw( )
{
    OWeakAggObject::release();
}
 
// XTypeProvider
 
namespace
{
    struct theSvxUnoTextRangeTypes :
        public rtl::StaticWithInit<uno::Sequence<uno::Type>, theSvxUnoTextRangeTypes>
    {
        uno::Sequence<uno::Type> operator () ()
        {
            uno::Sequence< uno::Type > aTypeSequence;
 
            aTypeSequence.realloc( 9 ); // !DANGER! keep this updated
            uno::Type* pTypes = aTypeSequence.getArray();
 
            *pTypes++ = cppu::UnoType<text::XTextRange>::get();
            *pTypes++ = cppu::UnoType<beans::XPropertySet>::get();
            *pTypes++ = cppu::UnoType<beans::XMultiPropertySet>::get();
            *pTypes++ = cppu::UnoType<beans::XMultiPropertyStates>::get();
            *pTypes++ = cppu::UnoType<beans::XPropertyState>::get();
            *pTypes++ = cppu::UnoType<lang::XServiceInfo>::get();
            *pTypes++ = cppu::UnoType<lang::XTypeProvider>::get();
            *pTypes++ = cppu::UnoType<lang::XUnoTunnel>::get();
            *pTypes++ = cppu::UnoType<text::XTextRangeCompare>::get();
 
            return aTypeSequence;
        }
    };
}
 
uno::Sequence< uno::Type > SAL_CALL SvxUnoTextRange::getTypes()
{
    return theSvxUnoTextRangeTypes::get();
}
 
uno::Sequence< sal_Int8 > SAL_CALL SvxUnoTextRange::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}
 
// XTextRange
uno::Reference< text::XText > SAL_CALL SvxUnoTextRange::getText()
{
    return xParentText;
}
 
// lang::XServiceInfo
OUString SAL_CALL SvxUnoTextRange::getImplementationName()
{
    return OUString("SvxUnoTextRange");
}
 
 
// class SvxUnoText
 
 
SvxUnoTextBase::SvxUnoTextBase(const SvxItemPropertySet* _pSet)
    : SvxUnoTextRangeBase(_pSet)
{
}
 
SvxUnoTextBase::SvxUnoTextBase(const SvxEditSource* pSource, const SvxItemPropertySet* _pSet, uno::Reference < text::XText > const & xParent)
    : SvxUnoTextRangeBase(pSource, _pSet)
{
    xParentText = xParent;
    ESelection aSelection;
    ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
    SetSelection( aSelection );
}
 
SvxUnoTextBase::SvxUnoTextBase(const SvxUnoTextBase& rText)
:   SvxUnoTextRangeBase( rText )
, text::XTextAppend()
,   text::XTextCopy()
,   container::XEnumerationAccess()
,   text::XTextRangeMover()
,   lang::XTypeProvider()
{
    xParentText = rText.xParentText;
}
 
SvxUnoTextBase::~SvxUnoTextBase() throw()
{
}
 
// XInterface
uno::Any SAL_CALL SvxUnoTextBase::queryAggregation( const uno::Type & rType )
{
    QUERYINT( text::XText );
    QUERYINT( text::XSimpleText );
    if( rType == cppu::UnoType<text::XTextRange>::get())
        return uno::makeAny(uno::Reference< text::XTextRange >(static_cast<text::XText*>(this)));
    QUERYINT(container::XEnumerationAccess );
    QUERYINT( container::XElementAccess );
    QUERYINT( beans::XMultiPropertyStates );
    QUERYINT( beans::XPropertySet );
    QUERYINT( beans::XMultiPropertySet );
    QUERYINT( beans::XPropertyState );
    QUERYINT( text::XTextRangeCompare );
    QUERYINT( lang::XServiceInfo );
    QUERYINT( text::XTextRangeMover );
    QUERYINT( text::XTextCopy );
    QUERYINT( text::XTextAppend );
    QUERYINT( text::XParagraphAppend );
    QUERYINT( text::XTextPortionAppend );
    QUERYINT( lang::XTypeProvider );
    QUERYINT( lang::XUnoTunnel );
 
    return uno::Any();
}
 
// XTypeProvider
 
namespace
{
    struct theSvxUnoTextBaseTypes :
        public rtl::StaticWithInit<uno::Sequence<uno::Type>, theSvxUnoTextBaseTypes>
    {
        uno::Sequence<uno::Type> operator () ()
        {
            uno::Sequence< uno::Type > aTypeSequence;
 
            aTypeSequence.realloc( 15 ); // !DANGER! keep this updated
            uno::Type* pTypes = aTypeSequence.getArray();
 
            *pTypes++ = cppu::UnoType<text::XText>::get();
            *pTypes++ = cppu::UnoType<container::XEnumerationAccess>::get();
            *pTypes++ = cppu::UnoType<beans::XPropertySet>::get();
            *pTypes++ = cppu::UnoType<beans::XMultiPropertySet>::get();
            *pTypes++ = cppu::UnoType<beans::XMultiPropertyStates>::get();
            *pTypes++ = cppu::UnoType<beans::XPropertyState>::get();
            *pTypes++ = cppu::UnoType<text::XTextRangeMover>::get();
            *pTypes++ = cppu::UnoType<text::XTextAppend>::get();
            *pTypes++ = cppu::UnoType<text::XTextCopy>::get();
            *pTypes++ = cppu::UnoType<text::XParagraphAppend>::get();
            *pTypes++ = cppu::UnoType<text::XTextPortionAppend>::get();
            *pTypes++ = cppu::UnoType<lang::XServiceInfo>::get();
            *pTypes++ = cppu::UnoType<lang::XTypeProvider>::get();
            *pTypes++ = cppu::UnoType<lang::XUnoTunnel>::get();
            *pTypes++ = cppu::UnoType<text::XTextRangeCompare>::get();
 
            return aTypeSequence;
        }
    };
}
 
uno::Sequence< uno::Type > SAL_CALL SvxUnoTextBase::getTypes()
{
    return theSvxUnoTextBaseTypes::get();
}
 
uno::Sequence< sal_Int8 > SAL_CALL SvxUnoTextBase::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}
 
uno::Reference< text::XTextCursor > SvxUnoTextBase::createTextCursorBySelection( const ESelection& rSel )
{
    SvxUnoTextCursor* pCursor = new SvxUnoTextCursor( *this );
    uno::Reference< text::XTextCursor >  xCursor( pCursor );
    pCursor->SetSelection( rSel );
    return xCursor;
}
 
// XSimpleText
 
uno::Reference< text::XTextCursor > SAL_CALL SvxUnoTextBase::createTextCursor()
{
    SolarMutexGuard aGuard;
    return new SvxUnoTextCursor( *this );
}
 
uno::Reference< text::XTextCursor > SAL_CALL SvxUnoTextBase::createTextCursorByRange( const uno::Reference< text::XTextRange >& aTextPosition )
{
    SolarMutexGuard aGuard;
 
    uno::Reference< text::XTextCursor >  xCursor;
 
    if( aTextPosition.is() )
    {
        SvxUnoTextRangeBase* pRange = SvxUnoTextRangeBase::getImplementation( aTextPosition );
        if(pRange)
            xCursor = createTextCursorBySelection( pRange->GetSelection() );
    }
 
    return xCursor;
}
 
void SAL_CALL SvxUnoTextBase::insertString( const uno::Reference< text::XTextRange >& xRange, const OUString& aString, sal_Bool bAbsorb )
{
    SolarMutexGuard aGuard;
 
    if( !xRange.is() )
        return;
 
    ESelection aSelection;
    if (GetEditSource())
    {
        ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
        SetSelection( aSelection );
    }
 
    SvxUnoTextRangeBase* pRange = SvxUnoTextRange::getImplementation( xRange );
    if(pRange)
    {
        // setString on SvxUnoTextRangeBase instead of itself QuickInsertText
        // and UpdateData, so that the selection will be adjusted to
        // SvxUnoTextRangeBase. Actually all cursor objects of this Text must
        // to be statement to be adapted!
 
        if (!bAbsorb)                   // do not replace -> append on tail
            pRange->CollapseToEnd();
 
        pRange->setString( aString );
 
        pRange->CollapseToEnd();
    }
}
 
void SAL_CALL SvxUnoTextBase::insertControlCharacter( const uno::Reference< text::XTextRange >& xRange, sal_Int16 nControlCharacter, sal_Bool bAbsorb )
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = GetEditSource() ? GetEditSource()->GetTextForwarder() : nullptr;
 
    if( pForwarder )
    {
        ESelection aSelection;
        ::GetSelection( aSelection, pForwarder );
        SetSelection( aSelection );
 
        switch( nControlCharacter )
        {
        case text::ControlCharacter::PARAGRAPH_BREAK:
        {
            const OUString aText( u'\x000D' );  // '\r' does not work on Mac
            insertString( xRange, aText, bAbsorb );
 
            return;
        }
        case text::ControlCharacter::LINE_BREAK:
        {
            SvxUnoTextRangeBase* pRange = SvxUnoTextRange::getImplementation( xRange );
            if(pRange)
            {
                ESelection aRange = pRange->GetSelection();
 
                if( bAbsorb )
                {
                    pForwarder->QuickInsertText( "", aRange );
 
                    aRange.nEndPos = aRange.nStartPos;
                    aRange.nEndPara = aRange.nStartPara;
                }
                else
                {
                    aRange.nStartPara = aRange.nEndPara;
                    aRange.nStartPos = aRange.nEndPos;
                }
 
                pForwarder->QuickInsertLineBreak( aRange );
                GetEditSource()->UpdateData();
 
                aRange.nEndPos += 1;
                if( !bAbsorb )
                    aRange.nStartPos += 1;
 
                pRange->SetSelection( aRange );
            }
            return;
        }
        case text::ControlCharacter::APPEND_PARAGRAPH:
        {
            SvxUnoTextRangeBase* pRange = SvxUnoTextRange::getImplementation( xRange );
            if(pRange)
            {
                ESelection aRange = pRange->GetSelection();
//              ESelection aOldSelection = aRange;
 
                aRange.nStartPos  = pForwarder->GetTextLen( aRange.nStartPara );
 
                aRange.nEndPara = aRange.nStartPara;
                aRange.nEndPos  = aRange.nStartPos;
 
                pRange->SetSelection( aRange );
                const OUString aText( u'\x000D' );  // '\r' does not work on Mac
                pRange->setString( aText );
 
                aRange.nStartPos = 0;
                aRange.nStartPara += 1;
                aRange.nEndPos = 0;
                aRange.nEndPara += 1;
 
                pRange->SetSelection( aRange );
 
                return;
            }
            SAL_FALLTHROUGH;
        }
        default:
            throw lang::IllegalArgumentException();
        }
    }
}
 
// XText
void SAL_CALL SvxUnoTextBase::insertTextContent( const uno::Reference< text::XTextRange >& xRange, const uno::Reference< text::XTextContent >& xContent, sal_Bool bAbsorb )
{
    SolarMutexGuard aGuard;
 
    SvxTextForwarder* pForwarder = GetEditSource() ? GetEditSource()->GetTextForwarder() : nullptr;
    if (!pForwarder)
        return;
 
    uno::Reference<beans::XPropertySet> xPropSet(xRange, uno::UNO_QUERY);
    if (!xPropSet.is())
        throw lang::IllegalArgumentException();
 
    uno::Any aAny = xPropSet->getPropertyValue(UNO_TR_PROP_SELECTION);
    text::TextRangeSelection aSel = aAny.get<text::TextRangeSelection>();
    if (!bAbsorb)
        aSel.Start = aSel.End;
 
    std::unique_ptr<SvxFieldData> pFieldData(SvxFieldData::Create(xContent));
    if (!pFieldData)
        throw lang::IllegalArgumentException();
 
    SvxFieldItem aField( *pFieldData, EE_FEATURE_FIELD );
    pForwarder->QuickInsertField(aField, toESelection(aSel));
    GetEditSource()->UpdateData();
 
    uno::Reference<beans::XPropertySet> xPropSetContent(xContent, uno::UNO_QUERY);
    if (!xContent.is())
        throw lang::IllegalArgumentException();
 
    xPropSetContent->setPropertyValue(UNO_TC_PROP_ANCHOR, uno::makeAny(xRange));
 
    aSel.End.PositionInParagraph += 1;
    aSel.Start.PositionInParagraph = aSel.End.PositionInParagraph;
    xPropSet->setPropertyValue(UNO_TR_PROP_SELECTION, uno::makeAny(aSel));
}
 
void SAL_CALL SvxUnoTextBase::removeTextContent( const uno::Reference< text::XTextContent >& )
{
}
 
// XTextRange
 
uno::Reference< text::XText > SAL_CALL SvxUnoTextBase::getText()
{
    SolarMutexGuard aGuard;
 
    if (GetEditSource())
    {
        ESelection aSelection;
        ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
        SetSelection( aSelection );
    }
 
    return static_cast<text::XText*>(this);
}
 
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::getStart()
{
    return SvxUnoTextRangeBase::getStart();
}
 
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::getEnd()
{
    return SvxUnoTextRangeBase::getEnd();
}
 
OUString SAL_CALL SvxUnoTextBase::getString()
{
    return SvxUnoTextRangeBase::getString();
}
 
void SAL_CALL SvxUnoTextBase::setString( const OUString& aString )
{
    SvxUnoTextRangeBase::setString(aString);
}
 
 
// XEnumerationAccess
uno::Reference< container::XEnumeration > SAL_CALL SvxUnoTextBase::createEnumeration()
{
    SolarMutexGuard aGuard;
    if( maSelection == ESelection(0,0,0,0) || maSelection == ESelection(EE_PARA_MAX_COUNT,0,0,0) )
    {
        ESelection aSelection;
        ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
        uno::Reference< container::XEnumeration > xEnum( static_cast<container::XEnumeration*>( new SvxUnoTextContentEnumeration( *this, aSelection ) ) );
        return xEnum;
    }
    else
    {
        uno::Reference< container::XEnumeration > xEnum( static_cast<container::XEnumeration*>( new SvxUnoTextContentEnumeration( *this, maSelection ) ) );
        return xEnum;
    }
}
 
// XElementAccess ( container::XEnumerationAccess )
uno::Type SAL_CALL SvxUnoTextBase::getElementType(  )
{
    return cppu::UnoType<text::XTextRange>::get();
}
 
sal_Bool SAL_CALL SvxUnoTextBase::hasElements(  )
{
    SolarMutexGuard aGuard;
 
    if(GetEditSource())
    {
        SvxTextForwarder* pForwarder = GetEditSource()->GetTextForwarder();
        if(pForwarder)
            return pForwarder->GetParagraphCount() != 0;
    }
 
    return false;
}
 
// text::XTextRangeMover
void SAL_CALL SvxUnoTextBase::moveTextRange( const uno::Reference< text::XTextRange >&, sal_Int16 )
{
}
 
/// @throws lang::IllegalArgumentException
/// @throws beans::UnknownPropertyException
/// @throws uno::RuntimeException
void SvxPropertyValuesToItemSet(
        SfxItemSet &rItemSet,
        const uno::Sequence< beans::PropertyValue >& rPropertyVaules,
        const SfxItemPropertySet *pPropSet,
        SvxTextForwarder *pForwarder /*needed for WID_NUMLEVEL*/,
        sal_Int32 nPara /*needed for WID_NUMLEVEL*/)
{
    for (const beans::PropertyValue& rProp : rPropertyVaules)
    {
        const SfxItemPropertySimpleEntry *pEntry = pPropSet->getPropertyMap().getByName( rProp.Name );
        if (!pEntry)
            throw beans::UnknownPropertyException( "Unknown property: " + rProp.Name, static_cast < cppu::OWeakObject * > ( nullptr ) );
        // Note: there is no need to take special care of the properties
        //      TextField (EE_FEATURE_FIELD) and
        //      TextPortionType (WID_PORTIONTYPE)
        //  since they are read-only and thus are already taken care of below.
 
        if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
            // should be PropertyVetoException which is not yet defined for the new import API's functions
            throw uno::RuntimeException("Property is read-only: " + rProp.Name, static_cast < cppu::OWeakObject * > ( nullptr ) );
            //throw PropertyVetoException ("Property is read-only: " + rProp.Name, static_cast < cppu::OWeakObject * > ( 0 ) );
 
        if (pEntry->nWID == WID_FONTDESC)
        {
            awt::FontDescriptor aDesc;
            if (rProp.Value >>= aDesc)
                SvxUnoFontDescriptor::FillItemSet( aDesc, rItemSet );
        }
        else if (pEntry->nWID == WID_NUMLEVEL)
        {
            if (pForwarder)
            {
                sal_Int16 nLevel = -1;
                rProp.Value >>= nLevel;
 
                // #101004# Call interface method instead of unsafe cast
                if (!pForwarder->SetDepth( nPara, nLevel ))
                    throw lang::IllegalArgumentException();
            }
        }
        else if (pEntry->nWID == WID_NUMBERINGSTARTVALUE )
        {
            if( pForwarder )
            {
                sal_Int16 nStartValue = -1;
                if( !(rProp.Value >>= nStartValue) )
                    throw lang::IllegalArgumentException();
 
                pForwarder->SetNumberingStartValue( nPara, nStartValue );
            }
        }
        else if (pEntry->nWID == WID_PARAISNUMBERINGRESTART )
        {
            if( pForwarder )
            {
                bool bParaIsNumberingRestart = false;
                if( !(rProp.Value >>= bParaIsNumberingRestart) )
                    throw lang::IllegalArgumentException();
 
                pForwarder->SetParaIsNumberingRestart( nPara, bParaIsNumberingRestart );
            }
        }
        else
            pPropSet->setPropertyValue( rProp.Name, rProp.Value, rItemSet );
    }
}
 
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::finishParagraphInsert(
        const uno::Sequence< beans::PropertyValue >& /*rCharAndParaProps*/,
        const uno::Reference< text::XTextRange >& /*rTextRange*/ )
{
    uno::Reference< text::XTextRange > xRet;
    return xRet;
}
 
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::finishParagraph(
        const uno::Sequence< beans::PropertyValue >& rCharAndParaProps )
{
    SolarMutexGuard aGuard;
 
    uno::Reference< text::XTextRange > xRet;
    SvxEditSource *pEditSource = GetEditSource();
    SvxTextForwarder *pTextForwarder = pEditSource ? pEditSource->GetTextForwarder() : nullptr;
    if (pTextForwarder)
    {
        sal_Int32 nParaCount = pTextForwarder->GetParagraphCount();
        DBG_ASSERT( nParaCount > 0, "paragraph count is 0 or negative" );
        pTextForwarder->AppendParagraph();
 
        // set properties for the previously last paragraph
        sal_Int32 nPara = nParaCount - 1;
        ESelection aSel( nPara, 0, nPara, 0 );
        SfxItemSet aItemSet( *pTextForwarder->GetEmptyItemSetPtr() );
        SvxPropertyValuesToItemSet( aItemSet, rCharAndParaProps,
                ImplGetSvxUnoOutlinerTextCursorSfxPropertySet(), pTextForwarder, nPara );
        pTextForwarder->QuickSetAttribs( aItemSet, aSel );
        pEditSource->UpdateData();
        SvxUnoTextRange* pRange = new SvxUnoTextRange( *this );
        xRet = pRange;
        pRange->SetSelection( aSel );
    }
    return xRet;
}
 
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::insertTextPortion(
        const OUString& /*rText*/,
        const uno::Sequence< beans::PropertyValue >& /*rCharAndParaProps*/,
        const uno::Reference< text::XTextRange>& /*rTextRange*/ )
{
    uno::Reference< text::XTextRange > xRet;
    return xRet;
}
 
// css::text::XTextPortionAppend (new import API)
uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::appendTextPortion(
        const OUString& rText,
        const uno::Sequence< beans::PropertyValue >& rCharAndParaProps )
{
    SolarMutexGuard aGuard;
 
    SvxEditSource *pEditSource = GetEditSource();
    SvxTextForwarder *pTextForwarder = pEditSource ? pEditSource->GetTextForwarder() : nullptr;
    uno::Reference< text::XTextRange > xRet;
    if (pTextForwarder)
    {
        sal_Int32 nParaCount = pTextForwarder->GetParagraphCount();
        DBG_ASSERT( nParaCount > 0, "paragraph count is 0 or negative" );
        sal_Int32 nPara = nParaCount - 1;
        SfxItemSet aSet( pTextForwarder->GetParaAttribs( nPara ) );
        sal_Int32 nStart = pTextForwarder->AppendTextPortion( nPara, rText, aSet );
        pEditSource->UpdateData();
        sal_Int32 nEnd   = pTextForwarder->GetTextLen( nPara );
 
        // set properties for the new text portion
        ESelection aSel( nPara, nStart, nPara, nEnd );
        pTextForwarder->RemoveAttribs( aSel );
        pEditSource->UpdateData();
 
        SfxItemSet aItemSet( *pTextForwarder->GetEmptyItemSetPtr() );
        SvxPropertyValuesToItemSet( aItemSet, rCharAndParaProps,
                ImplGetSvxTextPortionSfxPropertySet(), pTextForwarder, nPara );
        pTextForwarder->QuickSetAttribs( aItemSet, aSel );
        SvxUnoTextRange* pRange = new SvxUnoTextRange( *this );
        xRet = pRange;
        pRange->SetSelection( aSel );
        for( const beans::PropertyValue& rProp : rCharAndParaProps )
            pRange->setPropertyValue( rProp.Name, rProp.Value );
    }
    return xRet;
}
 
void SvxUnoTextBase::copyText(
    const uno::Reference< text::XTextCopy >& xSource )
{
    SolarMutexGuard aGuard;
    uno::Reference< lang::XUnoTunnel > xUT( xSource, uno::UNO_QUERY );
    SvxEditSource *pEditSource = GetEditSource();
    SvxTextForwarder *pTextForwarder = pEditSource ? pEditSource->GetTextForwarder() : nullptr;
    if( !pTextForwarder )
        return;
    if( xUT.is() )
    {
        SvxUnoTextBase* pSource = reinterpret_cast<SvxUnoTextBase*>(sal::static_int_cast<sal_uIntPtr>(
                                                                    xUT->getSomething( SvxUnoTextBase::getUnoTunnelId())));
        SvxEditSource *pSourceEditSource = pSource->GetEditSource();
        SvxTextForwarder *pSourceTextForwarder = pSourceEditSource ? pSourceEditSource->GetTextForwarder() : nullptr;
        if( pSourceTextForwarder )
        {
            pTextForwarder->CopyText( *pSourceTextForwarder );
            pEditSource->UpdateData();
        }
    }
    else
    {
        uno::Reference< text::XText > xSourceText( xSource, uno::UNO_QUERY );
        if( xSourceText.is() )
        {
            setString( xSourceText->getString() );
        }
    }
}
 
// lang::XServiceInfo
OUString SAL_CALL SvxUnoTextBase::getImplementationName()
{
    return OUString("SvxUnoTextBase");
}
 
uno::Sequence< OUString > SAL_CALL SvxUnoTextBase::getSupportedServiceNames(  )
{
    return getSupportedServiceNames_Static();
}
 
uno::Sequence< OUString > SAL_CALL SvxUnoTextBase::getSupportedServiceNames_Static(  )
{
    uno::Sequence< OUString > aSeq( SvxUnoTextRangeBase::getSupportedServiceNames_Static() );
    comphelper::ServiceInfoHelper::addToSequence( aSeq, {"com.sun.star.text.Text"} );
    return aSeq;
}
 
namespace
{
    class theSvxUnoTextBaseUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSvxUnoTextBaseUnoTunnelId > {};
}
 
const uno::Sequence< sal_Int8 > & SvxUnoTextBase::getUnoTunnelId() throw()
{
    return theSvxUnoTextBaseUnoTunnelId::get().getSeq();
}
 
SvxUnoTextBase* SvxUnoTextBase::getImplementation( const uno::Reference< uno::XInterface >& xInt )
{
    uno::Reference< lang::XUnoTunnel > xUT( xInt, uno::UNO_QUERY );
    if( xUT.is() )
        return reinterpret_cast<SvxUnoTextBase*>(sal::static_int_cast<sal_uIntPtr>(xUT->getSomething( SvxUnoTextBase::getUnoTunnelId())));
    else
        return nullptr;
}
 
sal_Int64 SAL_CALL SvxUnoTextBase::getSomething( const uno::Sequence< sal_Int8 >& rId )
{
    if( rId.getLength() == 16 && 0 == memcmp( getUnoTunnelId().getConstArray(),
                                                         rId.getConstArray(), 16 ) )
    {
        return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_uIntPtr>(this));
    }
    else
    {
        return SvxUnoTextRangeBase::getSomething( rId );
    }
}
 
SvxUnoText::SvxUnoText( const SvxItemPropertySet* _pSet ) throw()
: SvxUnoTextBase( _pSet )
{
}
 
SvxUnoText::SvxUnoText( const SvxEditSource* pSource, const SvxItemPropertySet* _pSet, uno::Reference < text::XText > const & xParent ) throw()
: SvxUnoTextBase( pSource, _pSet, xParent )
{
}
 
SvxUnoText::SvxUnoText( const SvxUnoText& rText ) throw()
: SvxUnoTextBase( rText )
, cppu::OWeakAggObject()
{
}
 
SvxUnoText::~SvxUnoText() throw()
{
}
 
// uno::XInterface
uno::Any SAL_CALL SvxUnoText::queryAggregation( const uno::Type & rType )
{
    uno::Any aAny( SvxUnoTextBase::queryAggregation( rType ) );
    if( !aAny.hasValue() )
        aAny = OWeakAggObject::queryAggregation( rType );
 
    return aAny;
}
 
uno::Any SAL_CALL SvxUnoText::queryInterface( const uno::Type & rType )
{
    return OWeakAggObject::queryInterface( rType );
}
 
void SAL_CALL SvxUnoText::acquire() throw( )
{
    OWeakAggObject::acquire();
}
 
void SAL_CALL SvxUnoText::release() throw( )
{
    OWeakAggObject::release();
}
 
// lang::XTypeProvider
uno::Sequence< uno::Type > SAL_CALL SvxUnoText::getTypes(  )
{
    return SvxUnoTextBase::getTypes();
}
 
uno::Sequence< sal_Int8 > SAL_CALL SvxUnoText::getImplementationId(  )
{
    return css::uno::Sequence<sal_Int8>();
}
 
namespace
{
    class theSvxUnoTextUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSvxUnoTextUnoTunnelId > {};
}
 
const uno::Sequence< sal_Int8 > & SvxUnoText::getUnoTunnelId() throw()
{
    return theSvxUnoTextUnoTunnelId::get().getSeq();
}
 
sal_Int64 SAL_CALL SvxUnoText::getSomething( const uno::Sequence< sal_Int8 >& rId )
{
    if( rId.getLength() == 16 && 0 == memcmp( getUnoTunnelId().getConstArray(),
                                                         rId.getConstArray(), 16 ) )
    {
        return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_uIntPtr>(this));
    }
    else
    {
        return SvxUnoTextBase::getSomething( rId );
    }
}
 
 
SvxDummyTextSource::~SvxDummyTextSource()
{
};
 
std::unique_ptr<SvxEditSource> SvxDummyTextSource::Clone() const
{
    return std::unique_ptr<SvxEditSource>(new SvxDummyTextSource);
}
 
SvxTextForwarder* SvxDummyTextSource::GetTextForwarder()
{
    return this;
}
 
void SvxDummyTextSource::UpdateData()
{
}
 
sal_Int32 SvxDummyTextSource::GetParagraphCount() const
{
    return 0;
}
 
sal_Int32 SvxDummyTextSource::GetTextLen( sal_Int32 ) const
{
    return 0;
}
 
OUString SvxDummyTextSource::GetText( const ESelection& ) const
{
    return OUString();
}
 
SfxItemSet SvxDummyTextSource::GetAttribs( const ESelection&, EditEngineAttribs ) const
{
    // Very dangerous: The former implementation used a SfxItemPool created on the
    // fly which of course was deleted again ASAP. Thus, the returned SfxItemSet was using
    // a deleted Pool by design.
    return SfxItemSet(EditEngine::GetGlobalItemPool());
}
 
SfxItemSet SvxDummyTextSource::GetParaAttribs( sal_Int32 ) const
{
    return GetAttribs(ESelection());
}
 
void SvxDummyTextSource::SetParaAttribs( sal_Int32, const SfxItemSet& )
{
}
 
void SvxDummyTextSource::RemoveAttribs( const ESelection& )
{
}
 
void SvxDummyTextSource::GetPortions( sal_Int32, std::vector<sal_Int32>& ) const
{
}
 
SfxItemState SvxDummyTextSource::GetItemState( const ESelection&, sal_uInt16 ) const
{
    return SfxItemState::UNKNOWN;
}
 
SfxItemState SvxDummyTextSource::GetItemState( sal_Int32, sal_uInt16 ) const
{
    return SfxItemState::UNKNOWN;
}
 
SfxItemPool* SvxDummyTextSource::GetPool() const
{
    return nullptr;
}
 
void SvxDummyTextSource::QuickInsertText( const OUString&, const ESelection& )
{
}
 
void SvxDummyTextSource::QuickInsertField( const SvxFieldItem&, const ESelection& )
{
}
 
void SvxDummyTextSource::QuickSetAttribs( const SfxItemSet&, const ESelection& )
{
}
 
void SvxDummyTextSource::QuickInsertLineBreak( const ESelection& )
{
};
 
OUString SvxDummyTextSource::CalcFieldValue( const SvxFieldItem&, sal_Int32, sal_Int32, boost::optional<Color>&, boost::optional<Color>& )
{
    return OUString();
}
 
void SvxDummyTextSource::FieldClicked( const SvxFieldItem&, sal_Int32, sal_Int32 )
{
}
 
bool SvxDummyTextSource::IsValid() const
{
    return false;
}
 
LanguageType SvxDummyTextSource::GetLanguage( sal_Int32, sal_Int32 ) const
{
    return LANGUAGE_DONTKNOW;
}
 
sal_Int32 SvxDummyTextSource::GetFieldCount( sal_Int32 ) const
{
    return 0;
}
 
EFieldInfo SvxDummyTextSource::GetFieldInfo( sal_Int32, sal_uInt16 ) const
{
    return EFieldInfo();
}
 
EBulletInfo SvxDummyTextSource::GetBulletInfo( sal_Int32 ) const
{
    return EBulletInfo();
}
 
tools::Rectangle SvxDummyTextSource::GetCharBounds( sal_Int32, sal_Int32 ) const
{
    return tools::Rectangle();
}
 
tools::Rectangle SvxDummyTextSource::GetParaBounds( sal_Int32 ) const
{
    return tools::Rectangle();
}
 
MapMode SvxDummyTextSource::GetMapMode() const
{
    return MapMode();
}
 
OutputDevice* SvxDummyTextSource::GetRefDevice() const
{
    return nullptr;
}
 
bool SvxDummyTextSource::GetIndexAtPoint( const Point&, sal_Int32&, sal_Int32& ) const
{
    return false;
}
 
bool SvxDummyTextSource::GetWordIndices( sal_Int32, sal_Int32, sal_Int32&, sal_Int32& ) const
{
    return false;
}
 
bool SvxDummyTextSource::GetAttributeRun( sal_Int32&, sal_Int32&, sal_Int32, sal_Int32, bool ) const
{
    return false;
}
 
sal_Int32 SvxDummyTextSource::GetLineCount( sal_Int32 ) const
{
    return 0;
}
 
sal_Int32 SvxDummyTextSource::GetLineLen( sal_Int32, sal_Int32 ) const
{
    return 0;
}
 
void SvxDummyTextSource::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 /*nParagraph*/, sal_Int32 /*nLine*/ ) const
{
    rStart = rEnd = 0;
}
 
sal_Int32 SvxDummyTextSource::GetLineNumberAtIndex( sal_Int32 /*nPara*/, sal_Int32 /*nIndex*/ ) const
{
    return 0;
}
 
bool SvxDummyTextSource::QuickFormatDoc( bool )
{
    return false;
}
 
sal_Int16 SvxDummyTextSource::GetDepth( sal_Int32 ) const
{
    return -1;
}
 
bool SvxDummyTextSource::SetDepth( sal_Int32, sal_Int16 nNewDepth )
{
    return nNewDepth == 0;
}
 
bool SvxDummyTextSource::Delete( const ESelection& )
{
    return false;
}
 
bool SvxDummyTextSource::InsertText( const OUString&, const ESelection& )
{
    return false;
}
 
const SfxItemSet * SvxDummyTextSource::GetEmptyItemSetPtr()
{
    return nullptr;
}
 
void SvxDummyTextSource::AppendParagraph()
{
}
 
sal_Int32 SvxDummyTextSource::AppendTextPortion( sal_Int32, const OUString &, const SfxItemSet & )
{
    return 0;
}
 
void  SvxDummyTextSource::CopyText(const SvxTextForwarder& )
{
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1004 The 'GetEditSource()' pointer was used unsafely after it was verified against nullptr. Check lines: 1854, 1873.

V547 Expression '!bUnknownPropertyFound' is always false.

V547 Expression '!bUnknownPropertyFound' is always true.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.