Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  txtfrm.cxx   Sprache: 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 .
 */


#include <config_wasm_strip.h>

#include <hintids.hxx>
#include <hints.hxx>
#include <svl/ctloptions.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/charhiddenitem.hxx>
#include <editeng/pgrditem.hxx>
#include <comphelper/configuration.hxx>
#include <swmodule.hxx>
#include <SwSmartTagMgr.hxx>
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <viewsh.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <paratr.hxx>
#include <viewopt.hxx>
#include <flyfrm.hxx>
#include <tabfrm.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <tgrditem.hxx>
#include <dbg_lay.hxx>
#include <fmtfld.hxx>
#include <fmtftn.hxx>
#include <txtfld.hxx>
#include <txtftn.hxx>
#include <ftninfo.hxx>
#include <fmtline.hxx>
#include <txtfrm.hxx>
#include <notxtfrm.hxx>
#include <sectfrm.hxx>
#include "itrform2.hxx"
#include "widorp.hxx"
#include "txtcache.hxx"
#include <fntcache.hxx>
#include <SwGrammarMarkUp.hxx>
#include <lineinfo.hxx>
#include <SwPortionHandler.hxx>
#include <dcontact.hxx>
#include <sortedobjs.hxx>
#include <txtflcnt.hxx>
#include <fmtflcnt.hxx>
#include <fmtcntnt.hxx>
#include <numrule.hxx>
#include <GrammarContact.hxx>
#include <calbck.hxx>
#include <ftnidx.hxx>
#include <ftnfrm.hxx>

#include <wrtsh.hxx>
#include <view.hxx>
#include <edtwin.hxx>
#include <FrameControlsManager.hxx>

namespace sw {

    MergedAttrIterBase::MergedAttrIterBase(SwTextFrame const& rFrame)
        : m_pMerged(rFrame.GetMergedPara())
        , m_pNode(m_pMerged ? nullptr : rFrame.GetTextNodeFirst())
        , m_CurrentExtent(0)
        , m_CurrentHint(0)
    {
    }

    SwTextAttr const* MergedAttrIter::NextAttr(SwTextNode const** ppNode)
    {
        if (m_pMerged)
        {
            while (m_CurrentExtent < m_pMerged->extents.size())
            {
                sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent]);
                if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
                {
                    while (m_CurrentHint < pHints->Count())
                    {
                        SwTextAttr *const pHint(pHints->Get(m_CurrentHint));
                        if (rExtent.nEnd < pHint->GetStart()
                                // <= if it has no end or isn't empty
                            || (rExtent.nEnd == pHint->GetStart()
                                && (!pHint->GetEnd()
                                    || *pHint->GetEnd() != pHint->GetStart())))
                        {
                            break;
                        }
                        ++m_CurrentHint;
                        if (rExtent.nStart <= pHint->GetStart())
                        {
                            if (ppNode)
                            {
                                *ppNode = rExtent.pNode;
                            }
                            return pHint;
                        }
                    }
                }
                ++m_CurrentExtent;
                if (m_CurrentExtent < m_pMerged->extents.size() &&
                    rExtent.pNode != m_pMerged->extents[m_CurrentExtent].pNode)
                {
                    m_CurrentHint = 0; // reset
                }
            }
            return nullptr;
        }
        else
        {
            SwpHints const*const pHints(m_pNode->GetpSwpHints());
            if (pHints)
            {
                if (m_CurrentHint < pHints->Count())
                {
                    SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
                    ++m_CurrentHint;
                    if (ppNode)
                    {
                        *ppNode = m_pNode;
                    }
                    return pHint;
                }
            }
            return nullptr;
        }
    }

    MergedAttrIterByEnd::MergedAttrIterByEnd(SwTextFrame const& rFrame)
        : m_pNode(rFrame.GetMergedPara() ? nullptr : rFrame.GetTextNodeFirst())
        , m_CurrentHint(0)
    {
        if (!m_pNode)
        {
            MergedAttrIterReverse iter(rFrame);
            SwTextNode const* pNode(nullptr);
            while (SwTextAttr const* pHint = iter.PrevAttr(&pNode))
            {
                m_Hints.emplace_back(pNode, pHint);
            }
        }
    }

    SwTextAttr const* MergedAttrIterByEnd::NextAttr(SwTextNode const*& rpNode)
    {
        if (m_pNode)
        {
            SwpHints const*const pHints(m_pNode->GetpSwpHints());
            if (pHints)
            {
                if (m_CurrentHint < pHints->Count())
                {
                    SwTextAttr const*const pHint(
                            pHints->GetSortedByEnd(m_CurrentHint));
                    ++m_CurrentHint;
                    rpNode = m_pNode;
                    return pHint;
                }
            }
            return nullptr;
        }
        else
        {
            if (m_CurrentHint < m_Hints.size())
            {
                auto const ret = m_Hints[m_Hints.size() - m_CurrentHint - 1];
                ++m_CurrentHint;
                rpNode = ret.first;
                return ret.second;
            }
            return nullptr;
        }
    }

    void MergedAttrIterByEnd::PrevAttr()
    {
        assert(0 < m_CurrentHint); // should only rewind as far as 0
        --m_CurrentHint;
    }

    MergedAttrIterReverse::MergedAttrIterReverse(SwTextFrame const& rFrame)
        : MergedAttrIterBase(rFrame)
    {
        if (m_pMerged)
        {
            m_CurrentExtent = m_pMerged->extents.size();
            SwpHints const*const pHints(0 < m_CurrentExtent
                ? m_pMerged->extents[m_CurrentExtent-1].pNode->GetpSwpHints()
                : nullptr);
            if (pHints)
            {
                pHints->SortIfNeedBe();
                m_CurrentHint = pHints->Count();
            }
        }
        else
        {
            if (SwpHints const*const pHints = m_pNode->GetpSwpHints())
            {
                pHints->SortIfNeedBe();
                m_CurrentHint = pHints->Count();
            }
        }
    }

    SwTextAttr const* MergedAttrIterReverse::PrevAttr(SwTextNode const** ppNode)
    {
        if (m_pMerged)
        {
            while (0 < m_CurrentExtent)
            {
                sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent-1]);
                if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
                {
                    while (0 < m_CurrentHint)
                    {
                        SwTextAttr *const pHint(
                                pHints->GetSortedByEnd(m_CurrentHint - 1));
                        if (pHint->GetAnyEnd() < rExtent.nStart
                                // <= if it has end and isn't empty
                            || (pHint->GetEnd()
                                && *pHint->GetEnd() != pHint->GetStart()
                                && *pHint->GetEnd() == rExtent.nStart))
                        {
                            break;
                        }
                        --m_CurrentHint;
                        if (pHint->GetAnyEnd() <= rExtent.nEnd)
                        {
                            if (ppNode)
                            {
                                *ppNode = rExtent.pNode;
                            }
                            return pHint;
                        }
                    }
                }
                --m_CurrentExtent;
                if (0 < m_CurrentExtent &&
                    rExtent.pNode != m_pMerged->extents[m_CurrentExtent-1].pNode)
                {
                    SwpHints const*const pHints(
                        m_pMerged->extents[m_CurrentExtent-1].pNode->GetpSwpHints());
                    m_CurrentHint = pHints ? pHints->Count() : 0; // reset
                    if (pHints)
                        pHints->SortIfNeedBe();
                }
            }
            return nullptr;
        }
        else
        {
            SwpHints const*const pHints(m_pNode->GetpSwpHints());
            if (pHints && 0 < m_CurrentHint)
            {
                SwTextAttr const*const pHint(pHints->GetSortedByEnd(m_CurrentHint - 1));
                --m_CurrentHint;
                if (ppNode)
                {
                    *ppNode = m_pNode;
                }
                return pHint;
            }
            return nullptr;
        }
    }

    bool FrameContainsNode(SwContentFrame const& rFrame, SwNodeOffset const nNodeIndex)
    {
        if (rFrame.IsTextFrame())
        {
            SwTextFrame const& rTextFrame(static_cast<SwTextFrame const&>(rFrame));
            if (sw::MergedPara const*const pMerged = rTextFrame.GetMergedPara())
            {
                SwNodeOffset const nFirst(pMerged->pFirstNode->GetIndex());
                SwNodeOffset const nLast(pMerged->pLastNode->GetIndex());
                return (nFirst <= nNodeIndex && nNodeIndex <= nLast);
            }
            else
            {
                return rTextFrame.GetTextNodeFirst()->GetIndex() == nNodeIndex;
            }
        }
        else
        {
            assert(rFrame.IsNoTextFrame());
            return static_cast<SwNoTextFrame const&>(rFrame).GetNode()->GetIndex() == nNodeIndex;
        }
    }

    bool IsParaPropsNode(SwRootFrame const& rLayout, SwTextNode const& rNode)
    {
        if (rLayout.HasMergedParas())
        {
            if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(rNode.getLayoutFrame(&rLayout)))
            {
                sw::MergedPara const*const pMerged(pFrame->GetMergedPara());
                if (pMerged && pMerged->pParaPropsNode != &rNode)
                {
                    return false;
                }
            }
        }
        return true;
    }

    SwTextNode *
    GetParaPropsNode(SwRootFrame const& rLayout, SwNode const& rPos)
    {
        const SwTextNode *const pTextNode(rPos.GetTextNode());
        if (pTextNode && !sw::IsParaPropsNode(rLayout, *pTextNode))
        {
            return static_cast<SwTextFrame*>(pTextNode->getLayoutFrame(&rLayout))->GetMergedPara()->pParaPropsNode;
        }
        else
        {
            return const_cast<SwTextNode*>(pTextNode);
        }
    }

    SwPosition
    GetParaPropsPos(SwRootFrame const& rLayout, SwPosition const& rPos)
    {
        SwPosition pos(rPos);
        SwTextNode const*const pNode(pos.GetNode().GetTextNode());
        if (pNode)
            pos.Assign( *sw::GetParaPropsNode(rLayout, *pNode) );
        return pos;
    }

    std::pair<SwTextNode *, SwTextNode *>
    GetFirstAndLastNode(SwRootFrame const& rLayout, SwNode const& rPos)
    {
        SwTextNode *const pTextNode(const_cast<SwTextNode*>(rPos.GetTextNode()));
        if (pTextNode && rLayout.HasMergedParas())
        {
            if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(pTextNode->getLayoutFrame(&rLayout)))
            {
                if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
                {
                    return std::make_pair(pMerged->pFirstNode, const_cast<SwTextNode*>(pMerged->pLastNode));
                }
            }
        }
        return std::make_pair(pTextNode, pTextNode);
    }

    SwTextNode const& GetAttrMerged(SfxItemSet & rFormatSet,
            SwTextNode const& rNode, SwRootFrame const*const pLayout)
    {
        rNode.SwContentNode::GetAttr(rFormatSet);
        if (pLayout && pLayout->HasMergedParas())
        {
            auto pFrame = static_cast<SwTextFrame*>(rNode.getLayoutFrame(pLayout));
            if (sw::MergedPara const*const pMerged = pFrame ? pFrame->GetMergedPara() : nullptr)
            {
                if (pMerged->pFirstNode != &rNode)
                {
                    rFormatSet.ClearItem(RES_PAGEDESC);
                    rFormatSet.ClearItem(RES_BREAK);
                    static_assert(RES_PAGEDESC + 1 == sal_uInt16(RES_BREAK),
                            "first-node items must be adjacent");
                    SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(*rFormatSet.GetPool());
                    pMerged->pFirstNode->SwContentNode::GetAttr(firstSet);
                    rFormatSet.Put(firstSet);

                }
                if (pMerged->pParaPropsNode != &rNode)
                {
                    for (sal_uInt16 i = RES_PARATR_BEGIN; i != RES_FRMATR_END; ++i)
                    {
                        if (i != RES_PAGEDESC && i != RES_BREAK)
                        {
                            rFormatSet.ClearItem(i);
                        }
                    }
                    for (sal_uInt16 i = XATTR_FILL_FIRST; i <= XATTR_FILL_LAST; ++i)
                    {
                        rFormatSet.ClearItem(i);
                    }
                    SfxItemSetFixed<RES_PARATR_BEGIN, RES_PAGEDESC,
                                   RES_BREAK+1, RES_FRMATR_END,
                                   XATTR_FILL_FIRST, XATTR_FILL_LAST+1>
                         propsSet(*rFormatSet.GetPool());
                    pMerged->pParaPropsNode->SwContentNode::GetAttr(propsSet);
                    rFormatSet.Put(propsSet);
                    return *pMerged->pParaPropsNode;
                }
                // keep all the CHRATR/UNKNOWNATR anyway...
            }
        }
        return rNode;
    }

// namespace sw

/// Switches width and height of the text frame
void SwTextFrame::SwapWidthAndHeight()
{
    {
        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);

        if ( ! mbIsSwapped )
        {
            const tools::Long nPrtOfstX = aPrt.Pos().X();
            aPrt.Pos().setX( aPrt.Pos().Y() );

            if( IsVertLR() )
            {
                aPrt.Pos().setY( nPrtOfstX );
            }
            else
            {
                aPrt.Pos().setY( getFrameArea().Width() - ( nPrtOfstX + aPrt.Width() ) );
            }
        }
        else
        {
            const tools::Long nPrtOfstY = aPrt.Pos().Y();
            aPrt.Pos().setY( aPrt.Pos().X() );

            if( IsVertLR() )
            {
                aPrt.Pos().setX( nPrtOfstY );
            }
            else
            {
                aPrt.Pos().setX( getFrameArea().Height() - ( nPrtOfstY + aPrt.Height() ) );
            }
        }

        const tools::Long nPrtWidth = aPrt.Width();
        aPrt.Width( aPrt.Height() );
        aPrt.Height( nPrtWidth );
    }

    {
        const tools::Long nFrameWidth = getFrameArea().Width();
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Width( aFrm.Height() );
        aFrm.Height( nFrameWidth );
    }

    mbIsSwapped = ! mbIsSwapped;
}

/**
 * Calculates the coordinates of a rectangle when switching from
 * horizontal to vertical layout.
 */

void SwTextFrame::SwitchHorizontalToVertical( SwRect& rRect ) const
{
    // calc offset inside frame
    tools::Long nOfstX, nOfstY;
    if ( IsVertLR() )
    {
        if (IsVertLRBT())
        {
            // X and Y offsets here mean the position of the point that will be the top left corner
            // after the switch.
            nOfstX = rRect.Left() + rRect.Width() - getFrameArea().Left();
            nOfstY = rRect.Top() - getFrameArea().Top();
        }
        else
        {
            nOfstX = rRect.Left() - getFrameArea().Left();
            nOfstY = rRect.Top() - getFrameArea().Top();
        }
    }
    else
    {
        nOfstX = rRect.Left() - getFrameArea().Left();
        nOfstY = rRect.Top() + rRect.Height() - getFrameArea().Top();
    }

    const tools::Long nWidth = rRect.Width();
    const tools::Long nHeight = rRect.Height();

    if ( IsVertLR() )
    {
        rRect.Left(getFrameArea().Left() + nOfstY);
    }
    else
    {
        if ( mbIsSwapped )
            rRect.Left( getFrameArea().Left() + getFrameArea().Height() - nOfstY );
        else
            // frame is rotated
            rRect.Left( getFrameArea().Left() + getFrameArea().Width() - nOfstY );
    }

    if (IsVertLRBT())
    {
        if (mbIsSwapped)
            rRect.Top(getFrameArea().Top() + getFrameArea().Width() - nOfstX);
        else
            rRect.Top(getFrameArea().Top() + getFrameArea().Height() - nOfstX);
    }
    else
        rRect.Top(getFrameArea().Top() + nOfstX);
    rRect.Width( nHeight );
    rRect.Height( nWidth );
}

/**
 * Calculates the coordinates of a point when switching from
 * horizontal to vertical layout.
 */

void SwTextFrame::SwitchHorizontalToVertical( Point& rPoint ) const
{
    if (IsVertLRBT())
    {
        // The horizontal origo is the top left corner, the LRBT origo is the
        // bottom left corner. Finally x and y has to be swapped.
        SAL_WARN_IF(!mbIsSwapped, "sw.core",
                    "SwTextFrame::SwitchHorizontalToVertical, IsVertLRBT, not swapped");
        Point aPoint(rPoint);
        rPoint.setX(getFrameArea().Left() + (aPoint.Y() - getFrameArea().Top()));
        // This would be bottom - x delta, but bottom is top + height, finally
        // width (and not height), as it's swapped.
        rPoint.setY(getFrameArea().Top() + getFrameArea().Width()
                    - (aPoint.X() - getFrameArea().Left()));
        return;
    }

    // calc offset inside frame
    const tools::Long nOfstX = rPoint.X() - getFrameArea().Left();
    const tools::Long nOfstY = rPoint.Y() - getFrameArea().Top();
    if ( IsVertLR() )
        rPoint.setX( getFrameArea().Left() + nOfstY );
    else
    {
        if ( mbIsSwapped )
            rPoint.setX( getFrameArea().Left() + getFrameArea().Height() - nOfstY );
        else
            // calc rotated coords
            rPoint.setX( getFrameArea().Left() + getFrameArea().Width() - nOfstY );
    }

    rPoint.setY( getFrameArea().Top() + nOfstX );
}

/**
 * Calculates the a limit value when switching from
 * horizontal to vertical layout.
 */

tools::Long SwTextFrame::SwitchHorizontalToVertical( tools::Long nLimit ) const
{
    Point aTmp( 0, nLimit );
    SwitchHorizontalToVertical( aTmp );
    return aTmp.X();
}

/**
 * Calculates the coordinates of a rectangle when switching from
 * vertical to horizontal layout.
 */

void SwTextFrame::SwitchVerticalToHorizontal( SwRect& rRect ) const
{
    tools::Long nOfstX;

    // calc offset inside frame
    if ( IsVertLR() )
        nOfstX = rRect.Left() - getFrameArea().Left();
    else
    {
        if ( mbIsSwapped )
            nOfstX = getFrameArea().Left() + getFrameArea().Height() - ( rRect.Left() + rRect.Width() );
        else
            nOfstX = getFrameArea().Left() + getFrameArea().Width() - ( rRect.Left() + rRect.Width() );
    }

    tools::Long nOfstY;
    if (IsVertLRBT())
    {
        // Note that mbIsSwapped only affects the frame area, not rRect, so rRect.Height() is used
        // here unconditionally.
        if (mbIsSwapped)
            nOfstY = getFrameArea().Top() + getFrameArea().Width() - (rRect.Top() + rRect.Height());
        else
            nOfstY = getFrameArea().Top() + getFrameArea().Height() - (rRect.Top() + rRect.Height());
    }
    else
        nOfstY = rRect.Top() - getFrameArea().Top();
    const tools::Long nWidth = rRect.Height();
    const tools::Long nHeight = rRect.Width();

    // calc rotated coords
    rRect.Left( getFrameArea().Left() + nOfstY );
    rRect.Top( getFrameArea().Top() + nOfstX );
    rRect.Width( nWidth );
    rRect.Height( nHeight );
}

/**
 * Calculates the coordinates of a point when switching from
 * vertical to horizontal layout.
 */

void SwTextFrame::SwitchVerticalToHorizontal( Point& rPoint ) const
{
    tools::Long nOfstX;

    // calc offset inside frame
    if ( IsVertLR() )
        // X offset is Y - left.
        nOfstX = rPoint.X() - getFrameArea().Left();
    else
    {
        // X offset is right - X.
        if ( mbIsSwapped )
            nOfstX = getFrameArea().Left() + getFrameArea().Height() - rPoint.X();
        else
            nOfstX = getFrameArea().Left() + getFrameArea().Width() - rPoint.X();
    }

    tools::Long nOfstY;
    if (IsVertLRBT())
    {
        // Y offset is bottom - Y.
        if (mbIsSwapped)
            nOfstY = getFrameArea().Top() + getFrameArea().Width() - rPoint.Y();
        else
            nOfstY = getFrameArea().Top() + getFrameArea().Height() - rPoint.Y();
    }
    else
        // Y offset is Y - top.
        nOfstY = rPoint.Y() - getFrameArea().Top();

    // calc rotated coords
    rPoint.setX( getFrameArea().Left() + nOfstY );
    rPoint.setY( getFrameArea().Top() + nOfstX );
}

/**
 * Calculates the a limit value when switching from
 * vertical to horizontal layout.
 */

tools::Long SwTextFrame::SwitchVerticalToHorizontal( tools::Long nLimit ) const
{
    Point aTmp( nLimit, 0 );
    SwitchVerticalToHorizontal( aTmp );
    return aTmp.Y();
}

SwFrameSwapper::SwFrameSwapper( const SwTextFrame* pTextFrame, bool bSwapIfNotSwapped )
    : pFrame( pTextFrame ), bUndo( false )
{
    if (pFrame->IsVertical() && bSwapIfNotSwapped != pFrame->IsSwapped())
    {
        bUndo = true;
        const_cast<SwTextFrame*>(pFrame)->SwapWidthAndHeight();
    }
}

SwFrameSwapper::~SwFrameSwapper()
{
    if ( bUndo )
        const_cast<SwTextFrame*>(pFrame)->SwapWidthAndHeight();
}

void SwTextFrame::SwitchLTRtoRTL( SwRect& rRect ) const
{
    SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));

    tools::Long nWidth = rRect.Width();
    rRect.Left( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) +
                getFramePrintArea().Width() - rRect.Right() - 1 );

    rRect.Width( nWidth );
}

void SwTextFrame::SwitchLTRtoRTL( Point& rPoint ) const
{
    SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));

    rPoint.setX( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) + getFramePrintArea().Width() - rPoint.X() - 1 );
}

SwLayoutModeModifier::SwLayoutModeModifier( const OutputDevice& rOutp ) :
        m_rOut( rOutp ), m_nOldLayoutMode( rOutp.GetLayoutMode() )
{
}

SwLayoutModeModifier::~SwLayoutModeModifier()
{
    const_cast<OutputDevice&>(m_rOut).SetLayoutMode( m_nOldLayoutMode );
}

void SwLayoutModeModifier::Modify( bool bChgToRTL )
{
    const_cast<OutputDevice&>(m_rOut).SetLayoutMode( bChgToRTL ?
                                         vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl :
                                         vcl::text::ComplexTextLayoutFlags::BiDiStrong );
}

void SwLayoutModeModifier::SetAuto()
{
    const vcl::text::ComplexTextLayoutFlags nNewLayoutMode = m_nOldLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong;
    const_cast<OutputDevice&>(m_rOut).SetLayoutMode( nNewLayoutMode );
}

SwDigitModeModifier::SwDigitModeModifier( const OutputDevice& rOutp, LanguageType eCurLang,
                                          SvtCTLOptions::TextNumerals eCTLTextNumerals ) :
        rOut( rOutp ), nOldLanguageType( rOutp.GetDigitLanguage() )
{
    LanguageType eLang = eCurLang;
    if (comphelper::IsFuzzing())
        eLang = LANGUAGE_ENGLISH_US;
    else
    {
        if ( SvtCTLOptions::NUMERALS_HINDI == eCTLTextNumerals )
            eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
        else if ( SvtCTLOptions::NUMERALS_ARABIC == eCTLTextNumerals )
            eLang = LANGUAGE_ENGLISH;
        else if ( SvtCTLOptions::NUMERALS_SYSTEM == eCTLTextNumerals )
            eLang = ::GetAppLanguage();
    }

    const_cast<OutputDevice&>(rOut).SetDigitLanguage( eLang );
}

SwDigitModeModifier::~SwDigitModeModifier()
{
    const_cast<OutputDevice&>(rOut).SetDigitLanguage( nOldLanguageType );
}

void SwTextFrame::Init()
{
    OSL_ENSURE( !IsLocked(), "+SwTextFrame::Init: this is locked." );
    if( !IsLocked() )
    {
        ClearPara();
        SetHasRotatedPortions(false);
        // set flags directly to save a ResetPreps call,
        // and thereby an unnecessary GetPara call
        // don't set bOrphan, bLocked or bWait to false!
        // bOrphan = bFlag7 = bFlag8 = false;
    }
}

SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* pSib,
        sw::FrameMode const eMode)
    : SwContentFrame( pNode, pSib )
    , mnAllLines( 0 )
    , mnThisLines( 0 )
    , mnFlyAnchorOfst( 0 )
    , mnFlyAnchorOfstNoWrap( 0 )
    , mnFlyAnchorVertOfstNoWrap( 0 )
    , mnFootnoteLine( 0 )
    , mnHeightOfLastLine( 0 )
    , mnAdditionalFirstLineOffset( 0 )
    , mnOffset( 0 )
    , mnNoHyphOffset( COMPLETE_STRING )
    , mnNoHyphEndZone( 0 )
    , mnCacheIndex( USHRT_MAX )
    , mbLocked( false )
    , mbWidow( false )
    , mbJustWidow( false )
    , mbEmpty( false )
    , mbInFootnoteConnect( false )
    , mbFootnote( false )
    , mbRepaint( false )
    , mbHasRotatedPortions( false )
    , mbFieldFollow( false )
    , mbHasAnimation( false )
    , mbIsSwapped( false )
    , mbFollowFormatAllowed( true )
{
    mnFrameType = SwFrameType::Txt;
    // note: this may call SwClientNotify if it's in a list so do it last
    // note: this may change this->pRegisteredIn to m_pMergedPara->listeners
    m_pMergedPara = CheckParaRedlineMerge(*this, *pNode, eMode);
}

void SwTextFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer) const
{
    SwContentFrame::dumpAsXmlAttributes(writer);

    const SwTextNode *pTextNode = GetTextNodeFirst();
    (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(pTextNode->GetIndex()) );

    OString aMode = "Horizontal"_ostr;
    if (IsVertLRBT())
    {
        aMode = "VertBTLR"_ostr;
    }
    else if (IsVertLR())
    {
        aMode = "VertLR"_ostr;
    }
    else if (IsVertical())
    {
        aMode = "Vertical"_ostr;
    }
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("WritingMode"), BAD_CAST(aMode.getStr()));
}

void SwTextFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("txt"));
    dumpAsXmlAttributes( writer );
    if ( HasFollow() )
        (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );

    if (m_pPrecede != nullptr)
        (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTextFrame*>(m_pPrecede)->GetFrameId() );

    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("offset"), BAD_CAST(OString::number(static_cast<sal_Int32>(mnOffset)).getStr()));

    sw::MergedPara const*const pMerged(GetMergedPara());
    if (pMerged)
    {
        (void)xmlTextWriterStartElement( writer, BAD_CAST( "merged" ) );
        (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "paraPropsNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(pMerged->pParaPropsNode->GetIndex()) );
        for (auto const& e : pMerged->extents)
        {
            (void)xmlTextWriterStartElement( writer, BAD_CAST( "extent" ) );
            (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(e.pNode->GetIndex()) );
            (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "start" ), "%" SAL_PRIdINT32, e.nStart );
            (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "end" ), "%" SAL_PRIdINT32, e.nEnd );
            (void)xmlTextWriterEndElement( writer );
        }
        (void)xmlTextWriterEndElement( writer );
    }

    (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
    dumpInfosAsXml(writer);
    (void)xmlTextWriterEndElement(writer);

    // Dump Anchored objects if any
    const SwSortedObjs* pAnchored = GetDrawObjs();
    if ( pAnchored && pAnchored->size() > 0 )
    {
        (void)xmlTextWriterStartElement( writer, BAD_CAST( "anchored" ) );

        for (SwAnchoredObject* pObject : *pAnchored)
        {
            pObject->dumpAsXml( writer );
        }

        (void)xmlTextWriterEndElement( writer );
    }

    // Dump the children
    OUString aText = GetText(  );
    for ( int i = 0; i < 32; i++ )
    {
        aText = aText.replace( i, '*' );
    }
    auto nTextOffset = static_cast<sal_Int32>(GetOffset());
    sal_Int32 nTextLength = aText.getLength() - nTextOffset;
    if (const SwTextFrame* pTextFrameFollow = GetFollow())
    {
        nTextLength = static_cast<sal_Int32>(pTextFrameFollow->GetOffset() - GetOffset());
    }
    if (nTextLength > 0)
    {
        OString aText8
            = OUStringToOString(aText.subView(nTextOffset, nTextLength), RTL_TEXTENCODING_UTF8);
        (void)xmlTextWriterWriteString( writer,
                reinterpret_cast<const xmlChar *>(aText8.getStr(  )) );
    }
    if (const SwParaPortion* pPara = GetPara())
    {
        (void)xmlTextWriterStartElement(writer, BAD_CAST("SwParaPortion"));
        TextFrameIndex nOffset(0);
        const OUString& rText = GetText();
        (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pPara);
        const SwLineLayout* pLine = pPara;
        if (IsFollow())
        {
            nOffset += GetOffset();
        }
        while (pLine)
        {
            (void)xmlTextWriterStartElement(writer, BAD_CAST("SwLineLayout"));
            pLine->dumpAsXmlAttributes(writer, rText, nOffset);
            const SwLinePortion* pPor = pLine->GetFirstPortion();
            while (pPor)
            {
                pPor->dumpAsXml(writer, rText, nOffset);
                pPor = pPor->GetNextPortion();
            }
            (void)xmlTextWriterEndElement(writer);
            pLine = pLine->GetNext();
        }
        (void)xmlTextWriterEndElement(writer);
    }

    (void)xmlTextWriterEndElement(writer);
}

namespace sw {

SwTextFrame * MakeTextFrame(SwTextNode & rNode, SwFrame *const pSibling,
        sw::FrameMode const eMode)
{
    return new SwTextFrame(&rNode, pSibling, eMode);
}

void RemoveFootnotesForNode(
        SwRootFrame const& rLayout, SwTextNode const& rTextNode,
        std::vector<std::pair<sal_Int32, sal_Int32>> const*const pExtents)
{
    if (pExtents && pExtents->empty())
    {
        return// nothing to do
    }
    const SwFootnoteIdxs &rFootnoteIdxs = rTextNode.GetDoc().GetFootnoteIdxs();
    size_t nPos = 0;
    SwNodeOffset const nIndex = rTextNode.GetIndex();
    rFootnoteIdxs.SeekEntry( rTextNode, &nPos );
    if (nPos < rFootnoteIdxs.size())
    {
        while (nPos > 0 && rTextNode == (rFootnoteIdxs[ nPos ]->GetTextNode()))
            --nPos;
        if (nPos || rTextNode != (rFootnoteIdxs[ nPos ]->GetTextNode()))
            ++nPos;
    }
    size_t iter(0);
    for ( ; nPos < rFootnoteIdxs.size(); ++nPos)
    {
        SwTextFootnote* pTextFootnote = rFootnoteIdxs[ nPos ];
        if (pTextFootnote->GetTextNode().GetIndex() > nIndex)
            break;
        if (pExtents)
        {
            while ((*pExtents)[iter].second <= pTextFootnote->GetStart())
            {
                ++iter;
                if (iter == pExtents->size())
                {
                    return;
                }
            }
            if (pTextFootnote->GetStart() < (*pExtents)[iter].first)
            {
                continue;
            }
        }
        pTextFootnote->DelFrames(&rLayout);
    }
}

// namespace sw

void SwTextFrame::DestroyImpl()
{
    // Remove associated SwParaPortion from s_pTextCache
    ClearPara();

    assert(!GetDoc().IsInDtor()); // this shouldn't be happening with ViewShell owning layout
    if (!GetDoc().IsInDtor() && HasFootnote())
    {
        if (m_pMergedPara)
        {
            SwTextNode const* pNode(nullptr);
            for (auto const& e : m_pMergedPara->extents)
            {
                if (e.pNode != pNode)
                {
                    pNode = e.pNode;
                    // sw_redlinehide: not sure if it's necessary to check
                    // if the nodes are still alive here, which would require
                    // accessing WriterMultiListener::m_vDepends
                    sw::RemoveFootnotesForNode(*getRootFrame(), *pNode, nullptr);
                }
            }
        }
        else
        {
            SwTextNode *const pNode(static_cast<SwTextNode*>(GetDep()));
            if (pNode)
            {
                sw::RemoveFootnotesForNode(*getRootFrame(), *pNode, nullptr);
            }
        }
    }

    if (!GetDoc().IsInDtor())
    {
        if (SwView* pView = GetActiveView())
            pView->GetEditWin().GetFrameControlsManager().RemoveControls(this);
    }

    SwContentFrame::DestroyImpl();
}

SwTextFrame::~SwTextFrame()
{
    RemoveFromCache();
}

namespace sw {

// 1. if real insert => correct nStart/nEnd for full nLen
// 2. if rl un-delete => do not correct nStart/nEnd but just include un-deleted
static TextFrameIndex UpdateMergedParaForInsert(MergedPara & rMerged,
        sw::ParagraphBreakMode const eMode, SwScriptInfo *const pScriptInfo,
        bool const isRealInsert,
        SwTextNode const& rNode, sal_Int32 const nIndex, sal_Int32 const nLen)
{
    assert(!isRealInsert || nLen); // can 0 happen? yes, for redline in empty node
    assert(nIndex <= rNode.Len());
    assert(nIndex + nLen <= rNode.Len());
    assert(rMerged.pFirstNode->GetIndex() <= rNode.GetIndex() && rNode.GetIndex() <= rMerged.pLastNode->GetIndex());
    if (!nLen)
    {
        return TextFrameIndex(0);
    }
    OUStringBuffer text(rMerged.mergedText);
    sal_Int32 nTFIndex(0); // index used for insertion at the end
    sal_Int32 nInserted(0);
    bool bInserted(false);
    bool bFoundNode(false);
    auto itInsert(rMerged.extents.end());
    for (auto it = rMerged.extents.begin(); it != rMerged.extents.end(); ++it)
    {
        if (it->pNode == &rNode)
        {
            if (isRealInsert)
            {
                bFoundNode = true;
                if (it->nStart <= nIndex && nIndex <= it->nEnd)
                {   // note: this can happen only once
                    text.insert(nTFIndex + (nIndex - it->nStart),
                            rNode.GetText().subView(nIndex, nLen));
                    it->nEnd += nLen;
                    nInserted = nLen;
                    assert(!bInserted);
                    bInserted = true;
                }
                else if (nIndex < it->nStart)
                {
                    if (itInsert == rMerged.extents.end())
                    {
                        itInsert = it;
                    }
                    it->nStart += nLen;
                    it->nEnd += nLen;
                }
            }
            else
            {
                assert(it == rMerged.extents.begin() || (it-1)->pNode != &rNode || (it-1)->nEnd < nIndex);
                if (nIndex + nLen < it->nStart)
                {
                    itInsert = it;
                    break;
                }
                if (nIndex < it->nStart)
                {
                    text.insert(nTFIndex,
                        rNode.GetText().subView(nIndex, it->nStart - nIndex));
                    nInserted += it->nStart - nIndex;
                    it->nStart = nIndex;
                    bInserted = true;
                }
                assert(it->nStart <= nIndex);
                if (nIndex <= it->nEnd)
                {
                    nTFIndex += it->nEnd - it->nStart;
                    while (it->nEnd < nIndex + nLen)
                    {
                        auto *const pNext(
                            (it+1) != rMerged.extents.end() && (it+1)->pNode == it->pNode
                                ? &*(it+1)
                                : nullptr);
                        if (pNext && pNext->nStart <= nIndex + nLen)
                        {
                            text.insert(nTFIndex,
                                rNode.GetText().subView(it->nEnd, pNext->nStart - it->nEnd));
                            nTFIndex += pNext->nStart - it->nEnd;
                            nInserted += pNext->nStart - it->nEnd;
                            pNext->nStart = it->nStart;
                            it = rMerged.extents.erase(it);
                        }
                        else
                        {
                            text.insert(nTFIndex,
                                rNode.GetText().subView(it->nEnd, nIndex + nLen - it->nEnd));
                            nTFIndex += nIndex + nLen - it->nEnd;
                            nInserted += nIndex + nLen - it->nEnd;
                            it->nEnd = nIndex + nLen;
                        }
                    }
                    bInserted = true;
                    break;
                }
            }
        }
        else if (rNode.GetIndex() < it->pNode->GetIndex() || bFoundNode)
        {
            if (itInsert == rMerged.extents.end())
            {
                itInsert = it;
            }
            break;
        }
        if (itInsert == rMerged.extents.end())
        {
            nTFIndex += it->nEnd - it->nStart;
        }
    }
//    assert((bFoundNode || rMerged.extents.empty()) && "text node not found - why is it sending hints to us");
    if (!bInserted)
    {   // must be in a gap
        rMerged.extents.emplace(itInsert, const_cast<SwTextNode*>(&rNode), nIndex, nIndex + nLen);
        text.insert(nTFIndex, rNode.GetText().subView(nIndex, nLen));
        nInserted = nLen;
        // called from SwRangeRedline::InvalidateRange()
        if (rNode.GetRedlineMergeFlag() == SwNode::Merge::Hidden)
        {
            const_cast<SwTextNode&>(rNode).SetRedlineMergeFlag(SwNode::Merge::NonFirst);
        }
    }
    rMerged.mergedText = text.makeStringAndClear();
    if ((!bInserted && rMerged.extents.size() == 1) // also if it was empty!
        || rNode.GetIndex() <= rMerged.pParaPropsNode->GetIndex())
    {   // text inserted before current para-props node
        SwTextNode *const pOldParaPropsNode{rMerged.pParaPropsNode};
        FindParaPropsNodeIgnoreHidden(rMerged, eMode, pScriptInfo);
        if (rMerged.pParaPropsNode != pOldParaPropsNode)
        {
            pOldParaPropsNode->RemoveFromListRLHidden();
            rMerged.pParaPropsNode->AddToListRLHidden();
        }
    }
    return TextFrameIndex(nInserted);
}

// 1. if real delete => correct nStart/nEnd for full nLen
// 2. if rl delete => do not correct nStart/nEnd but just exclude deleted
TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
        sw::ParagraphBreakMode const eMode, SwScriptInfo *const pScriptInfo,
        bool const isRealDelete,
        SwTextNode const& rNode, sal_Int32 nIndex, sal_Int32 const nLen)
{
    assert(nIndex <= rNode.Len());
    assert(rMerged.pFirstNode->GetIndex() <= rNode.GetIndex() && rNode.GetIndex() <= rMerged.pLastNode->GetIndex());
    OUStringBuffer text(rMerged.mergedText);
    sal_Int32 nTFIndex(0);
    sal_Int32 nToDelete(nLen);
    sal_Int32 nDeleted(0);
    size_t nFoundNode(0);
//    size_t nErased(0);
    auto it = rMerged.extents.begin();
    for (; it != rMerged.extents.end(); )
    {
        bool bErase(false);
        if (it->pNode == &rNode)
        {
            ++nFoundNode;
            if (nIndex + nToDelete < it->nStart)
            {
                nToDelete = 0;
                if (!isRealDelete)
                {
                    break;
                }
                it->nStart -= nLen;
                it->nEnd -= nLen;
            }
            else
            {
                if (nIndex < it->nStart)
                {
                    // do not adjust nIndex into the text frame index space!
                    nToDelete -= it->nStart - nIndex;
                    nIndex = it->nStart;
                    // note: continue with the if check below, no else!
                }
                if (it->nStart <= nIndex && nIndex < it->nEnd)
                {
                    sal_Int32 const nDeleteHere(nIndex + nToDelete <= it->nEnd
                            ? nToDelete
                            : it->nEnd - nIndex);
                    text.remove(nTFIndex + (nIndex - it->nStart), nDeleteHere);
                    bErase = nDeleteHere == it->nEnd - it->nStart;
                    if (bErase)
                    {
//                        ++nErased;
                        assert(it->nStart == nIndex);
                        it = rMerged.extents.erase(it);
                    }
                    else if (isRealDelete)
                    {   // adjust for deleted text
                        it->nStart -= (nLen - nToDelete);
                        it->nEnd -= (nLen - nToDelete + nDeleteHere);
                        if (it != rMerged.extents.begin()
                            && (it-1)->pNode == &rNode
                            && (it-1)->nEnd == it->nStart)
                        {   // merge adjacent extents
                            nTFIndex += it->nEnd - it->nStart;
                            (it-1)->nEnd = it->nEnd;
                            it = rMerged.extents.erase(it);
                            bErase = true// skip increment
                        }
                    }
                    else
                    {   // exclude text marked as deleted
                        if (nIndex + nDeleteHere == it->nEnd)
                        {
                            it->nEnd -= nDeleteHere;
                        }
                        else
                        {
                            if (nIndex == it->nStart)
                            {
                                it->nStart += nDeleteHere;
                            }
                            else
                            {
                                sal_Int32 const nOldEnd(it->nEnd);
                                it->nEnd = nIndex;
                                it = rMerged.extents.emplace(it+1,
                                    it->pNode, nIndex + nDeleteHere, nOldEnd);
                            }
                            assert(nDeleteHere == nToDelete);
                        }
                    }
                    nDeleted += nDeleteHere;
                    nToDelete -= nDeleteHere;
                    nIndex += nDeleteHere;
                    if (!isRealDelete && nToDelete == 0)
                    {
                        break;
                    }
                }
            }
        }
        else if (nFoundNode != 0)
        {
            break;
        }
        if (!bErase)
        {
            nTFIndex += it->nEnd - it->nStart;
            ++it;
        }
    }
//    assert(nFoundNode != 0 && "text node not found - why is it sending hints to us");
    assert(nIndex <= rNode.Len() + nLen);
    // if there's a remaining deletion, it must be in gap at the end of the node
// can't do: might be last one in node was erased   assert(nLen == 0 || rMerged.empty() || (it-1)->nEnd <= nIndex);
    // note: if first node gets deleted then that must call DelFrames as
    // pFirstNode is never updated
    rMerged.mergedText = text.makeStringAndClear();
// could be all-hidden now so always check!    if (nErased && nErased == nFoundNode)
    {   // all visible text from node was erased
#if 1
        if (rMerged.pParaPropsNode == &rNode)
        {
            SwTextNode *const pOldParaPropsNode{rMerged.pParaPropsNode};
            FindParaPropsNodeIgnoreHidden(rMerged, eMode, pScriptInfo);
            if (rMerged.pParaPropsNode != pOldParaPropsNode)
            {
                pOldParaPropsNode->RemoveFromListRLHidden();
                rMerged.pParaPropsNode->AddToListRLHidden();
            }
        }
#endif
// NOPE must listen on all non-hidden nodes; particularly on pLastNode        rMerged.listener.EndListening(&const_cast<SwTextNode&>(rNode));
    }
    return TextFrameIndex(nDeleted);
}

std::pair<SwTextNode*, sal_Int32>
MapViewToModel(MergedPara const& rMerged, TextFrameIndex const i_nIndex)
{
    sal_Int32 nIndex(i_nIndex);
    sw::Extent const* pExtent(nullptr);
    for (const auto& rExt : rMerged.extents)
    {
        pExtent = &rExt;
        if (nIndex < (pExtent->nEnd - pExtent->nStart))
        {
            return std::make_pair(pExtent->pNode, pExtent->nStart + nIndex);
        }
        nIndex = nIndex - (pExtent->nEnd - pExtent->nStart);
    }
    assert(nIndex == 0 && "view index out of bounds");
    return pExtent
        ? std::make_pair(pExtent->pNode, pExtent->nEnd) //1-past-the-end index
        : std::make_pair(const_cast<SwTextNode*>(rMerged.pLastNode), rMerged.pLastNode->Len());
}

TextFrameIndex MapModelToView(MergedPara const& rMerged, SwTextNode const*const pNode, sal_Int32 const nIndex)
{
    assert(rMerged.pFirstNode->GetIndex() <= pNode->GetIndex()
        && pNode->GetIndex() <= rMerged.pLastNode->GetIndex());
    sal_Int32 nRet(0);
    bool bFoundNode(false);
    for (auto const& e : rMerged.extents)
    {
        if (pNode->GetIndex() < e.pNode->GetIndex())
        {
            return TextFrameIndex(nRet);
        }
        if (e.pNode == pNode)
        {
            if (e.nStart <= nIndex && nIndex <= e.nEnd)
            {
                return TextFrameIndex(nRet + (nIndex - e.nStart));
            }
            else if (nIndex < e.nStart)
            {
                // in gap before this extent => map to 0 here TODO???
                return TextFrameIndex(nRet);
            }
            bFoundNode = true;
        }
        else if (bFoundNode)
        {
            break;
        }
        nRet += e.nEnd - e.nStart;
    }
    if (bFoundNode)
    {
        // must be in a gap at the end of the node
        assert(nIndex <= pNode->Len());
        return TextFrameIndex(nRet);
    }
    else if (rMerged.extents.empty())
    {
        assert(nIndex <= pNode->Len());
        return TextFrameIndex(0);
    }
    return TextFrameIndex(rMerged.mergedText.getLength());
}

// namespace sw

std::pair<SwTextNode*, sal_Int32>
SwTextFrame::MapViewToModel(TextFrameIndex const nIndex) const
{
//nope    assert(GetPara());
    sw::MergedPara const*const pMerged(GetMergedPara());
    if (pMerged)
    {
        return sw::MapViewToModel(*pMerged, nIndex);
    }
    else
    {
        return std::make_pair(static_cast<SwTextNode*>(const_cast<sw::BroadcastingModify*>(
                    SwFrame::GetDep())), sal_Int32(nIndex));
    }
}

SwPosition SwTextFrame::MapViewToModelPos(TextFrameIndex const nIndex) const
{
    std::pair<SwTextNode*, sal_Int32> const ret(MapViewToModel(nIndex));
    return SwPosition(*ret.first, ret.second);
}

TextFrameIndex SwTextFrame::MapModelToView(SwTextNode const*const pNode, sal_Int32 const nIndex) const
{
//nope    assert(GetPara());
    sw::MergedPara const*const pMerged(GetMergedPara());
    if (pMerged)
    {
        return sw::MapModelToView(*pMerged, pNode, nIndex);
    }
    else
    {
        return TextFrameIndex(nIndex);
    }
}

TextFrameIndex SwTextFrame::MapModelToViewPos(SwPosition const& rPos) const
{
    SwTextNode const*const pNode(rPos.GetNode().GetTextNode());
    sal_Int32 const nIndex(rPos.GetContentIndex());
    return MapModelToView(pNode, nIndex);
}

void SwTextFrame::SetMergedPara(std::unique_ptr<sw::MergedPara> p)
{
    SwTextNode *const pFirst(m_pMergedPara ? m_pMergedPara->pFirstNode : nullptr);
    m_pMergedPara = std::move(p);
    if (pFirst)
    {
        if (m_pMergedPara)
        {
            assert(pFirst == m_pMergedPara->pFirstNode);
        }
        else
        {
            pFirst->Add(*this); // must register at node again
        }
    }
    // postcondition: frame must be listening somewhere
    assert(m_pMergedPara || GetDep());
}

const OUString& SwTextFrame::GetText() const
{
//nope    assert(GetPara());
    sw::MergedPara const*const pMerged(GetMergedPara());
    if (pMerged)
        return pMerged->mergedText;
    else
        return static_cast<SwTextNode const*>(SwFrame::GetDep())->GetText();
}

SwTextNode const* SwTextFrame::GetTextNodeForParaProps() const
{
    // FIXME can GetPara be 0 ? yes... this is needed in  SwContentNotify::SwContentNotify() which is called before any formatting is started
//nope    assert(GetPara());
    sw::MergedPara const*const pMerged(GetMergedPara());
    if (pMerged)
    {
//        assert(pMerged->pFirstNode == pMerged->pParaPropsNode); // surprising news!
        return pMerged->pParaPropsNode;
    }
    else
        return static_cast<SwTextNode const*>(SwFrame::GetDep());
}

SwTextNode const* SwTextFrame::GetTextNodeForFirstText() const
{
    sw::MergedPara const*const pMerged(GetMergedPara());
    if (pMerged)
        return pMerged->extents.empty()
            ? pMerged->pFirstNode
            : pMerged->extents.front().pNode;
    else
        return static_cast<SwTextNode const*>(SwFrame::GetDep());
}

SwTextNode const* SwTextFrame::GetTextNodeFirst() const
{
//nope    assert(GetPara());
    sw::MergedPara const*const pMerged(GetMergedPara());
    if (pMerged)
        return pMerged->pFirstNode;
    else
        return static_cast<SwTextNode const*>(SwFrame::GetDep());
}

SwDoc const& SwTextFrame::GetDoc() const
{
    return GetTextNodeFirst()->GetDoc();
}

LanguageType SwTextFrame::GetLangOfChar(TextFrameIndex const nIndex,
        sal_uInt16 const nScript, bool const bNoChar, bool const bNoneIfNoHyphenation) const
{
    // a single character can be mapped uniquely!
    std::pair<SwTextNode const*, sal_Int32> const pos(MapViewToModel(nIndex));
    return pos.first->GetLang(pos.second, bNoChar ? 0 : 1, nScript, bNoneIfNoHyphenation);
}

void SwTextFrame::ResetPreps()
{
    if ( GetCacheIdx() != USHRT_MAX )
    {
        if (SwParaPortion *pPara = GetPara())
            pPara->ResetPreps();
    }
}

static auto FindCellFrame(SwFrame const* pLower) -> SwLayoutFrame const*
{
    while (pLower)
    {
        if (pLower->IsCellFrame())
        {
            return static_cast<SwLayoutFrame const*>(pLower);
        }
        pLower = pLower->GetUpper();
    }
    return nullptr;
}

bool SwTextFrame::IsHiddenNow() const
{
    SwFrameSwapper aSwapper( thistrue );

    if( !getFrameArea().Width() && isFrameAreaDefinitionValid() && GetUpper()->isFrameAreaDefinitionValid() ) // invalid when stack overflows (StackHack)!
    {
//        OSL_FAIL( "SwTextFrame::IsHiddenNow: thin frame" );
        return true;
    }

    // TODO: what is the above check good for and can it be removed?
    return IsHiddenNowImpl();
}

bool SwTextFrame::IsHiddenNowImpl() const
{
    if (SwContentFrame::IsHiddenNow())
        return true;

    bool bHiddenCharsHidePara(false);
    bool bHiddenParaField(false);
    if (m_pMergedPara)
    {
        TextFrameIndex nHiddenStart(COMPLETE_STRING);
        TextFrameIndex nHiddenEnd(0);
        bool hasHidden{false};
        if (auto const pScriptInfo = GetScriptInfo())
        {
            hasHidden = pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0),
                    nHiddenStart, nHiddenEnd);
        }
        else // ParaPortion is created in Format, but this is called earlier
        {
            SwScriptInfo aInfo;
            aInfo.InitScriptInfoHidden(*m_pMergedPara->pFirstNode, m_pMergedPara.get());
            hasHidden = aInfo.GetBoundsOfHiddenRange(TextFrameIndex(0),
                        nHiddenStart, nHiddenEnd);
        }
        if ((TextFrameIndex(0) == nHiddenStart
                && TextFrameIndex(GetText().getLength()) <= nHiddenEnd)
            // special case: GetBoundsOfHiddenRange doesn't assign!
            // but it does return that there *is* something hidden, in case
            // the frame is empty then the whole thing must be hidden
            || (hasHidden && m_pMergedPara->mergedText.isEmpty()))
        {
            bHiddenCharsHidePara = true;
        }
        sw::MergedAttrIter iter(*this);
        SwTextNode const* pNode(nullptr);
        int nNewResultWeight = 0;
        for (SwTextAttr const* pHint = iter.NextAttr(&pNode); pHint; pHint = iter.NextAttr(&pNode))
        {
            if (pHint->Which() == RES_TXTATR_FIELD)
            {
                // see also SwpHints::CalcHiddenParaField()
                const SwFormatField& rField = pHint->GetFormatField();
                int nCurWeight = pNode->GetDoc().FieldCanHideParaWeight(rField.GetField()->GetTyp()->Which());
                if (nCurWeight > nNewResultWeight)
                {
                    nNewResultWeight = nCurWeight;
                    bHiddenParaField = pNode->GetDoc().FieldHidesPara(*rField.GetField());
                }
                else if (nCurWeight == nNewResultWeight && bHiddenParaField)
                {
                    // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
                    // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
                    // care about fields of higher weight.
                    bHiddenParaField = pNode->GetDoc().FieldHidesPara(*rField.GetField());
                }
            }
        }
    }
    else
    {
        bHiddenCharsHidePara = static_cast<SwTextNode const*>(SwFrame::GetDep())->HasHiddenCharAttribute( true );
        bHiddenParaField = static_cast<SwTextNode const*>(SwFrame::GetDep())->IsHiddenByParaField();
    }
    if (bHiddenCharsHidePara && GetDoc().getIDocumentSettingAccess().get(
            DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
    {
        // apparently in Word it's always the last para marker that determines hidden?
        // even in case when they are merged by delete redline (it's obvious when they are merged by hidden-attribute
        SwTextNode const*const pNode{ m_pMergedPara
            ? m_pMergedPara->pLastNode
            : static_cast<SwTextNode const*>(SwFrame::GetDep()) };
        // Word ignores hidden formatting on the cell end marker
        bool isLastInCell{false};
        if (SwLayoutFrame const*const pCellFrame{FindCellFrame(this)})
        {
            SwContentFrame const* pNext{GetNextContentFrame()};
            // skip frame in hidden section ("this" is *not* in hidden section!)
            while (pNext && pNext->SwContentFrame::IsHiddenNow())
            {
                pNext = pNext->GetNextContentFrame();
            }
            isLastInCell = pNext == nullptr || !pCellFrame->IsAnLower(pNext);
        }
        if (!isLastInCell)
        {
            SwFormatAutoFormat const& rListAutoFormat{pNode->GetAttr(RES_PARATR_LIST_AUTOFMT)};
            std::shared_ptr<SfxItemSet> const pSet{rListAutoFormat.GetStyleHandle()};
            SvxCharHiddenItem const* pItem{pSet ? pSet->GetItemIfSet(RES_CHRATR_HIDDEN) : nullptr};
            if (!pItem)
            {
                // don't use node's mpAttrSet, it doesn't apply to para marker
                SwFormatColl const*const pStyle{pNode->GetFormatColl()};
                if (pStyle)
                {
                    pItem = &pStyle->GetFormatAttr(RES_CHRATR_HIDDEN);
                }
            }
            if (!pItem || !pItem->GetValue())
            {
                bHiddenCharsHidePara = false;
            }
        }
    }
    const SwViewShell* pVsh = getRootFrame()->GetCurrShell();

    if ( pVsh && ( bHiddenCharsHidePara || bHiddenParaField ) )
    {

        if (
             ( bHiddenParaField &&
               ( !pVsh->GetViewOptions()->IsShowHiddenPara() &&
                 !pVsh->GetViewOptions()->IsFieldName() ) ) ||
             ( bHiddenCharsHidePara &&
               !pVsh->GetViewOptions()->IsShowHiddenChar() ) )
        {
            // in order to put the cursor in the body text, one paragraph must
            // be visible - check this for the 1st body paragraph
            if (IsInDocBody() && FindPrevCnt() == nullptr)
            {
                for (SwContentFrame const* pNext = FindNextCnt(true);
                        pNext != nullptr; pNext = pNext->FindNextCnt(true))
                {
                    if (!pNext->IsHiddenNow())
                        return true;
                }
                SAL_INFO("sw.core""unhiding one body paragraph");
                return false;
            }
            return true;
        }
    }

    return false;
}

/// Removes Textfrm's attachments, when it's hidden
void SwTextFrame::HideHidden()
{
    OSL_ENSURE( !GetFollow() && IsHiddenNow(),
            "HideHidden on visible frame of hidden frame has follow" );

    HideFootnotes(GetOffset(), TextFrameIndex(COMPLETE_STRING));
    HideAndShowObjects();

    // format information is obsolete
    ClearPara();
}

void SwTextFrame::HideFootnotes(TextFrameIndex const nStart, TextFrameIndex const nEnd)
{
    SwPageFrame *pPage = nullptr;
    sw::MergedAttrIter iter(*this);
    SwTextNode const* pNode(nullptr);
    for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
    {
        if (pHt->Which() == RES_TXTATR_FTN)
        {
            TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
            if (nEnd < nIdx)
                break;
            if (nStart <= nIdx)
            {
                if (!pPage)
                    pPage = FindPageFrame();
                pPage->RemoveFootnote( thisstatic_cast<const SwTextFootnote*>(pHt) );
            }
        }
    }
}

/**
 * as-character anchored graphics, which are used for a graphic bullet list.
 * As long as these graphic bullet list aren't imported, do not hide a
 * at-character anchored object, if
 * (a) the document is an imported WW8 document -
 *     checked by checking certain compatibility options -
 * (b) the paragraph is the last content in the document and
 * (c) the anchor character is an as-character anchored graphic.
 */

bool sw_HideObj( const SwTextFrame& _rFrame,
                  const RndStdIds _eAnchorType,
                  SwFormatAnchor const& rFormatAnchor,
                  SwAnchoredObject* _pAnchoredObj )
{
    bool bRet( true );

    if (_eAnchorType == RndStdIds::FLY_AT_CHAR)
    {
        const IDocumentSettingAccess *const pIDSA = &_rFrame.GetDoc().getIDocumentSettingAccess();
        if ( !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) &&
             !pIDSA->get(DocumentSettingId::OLD_LINE_SPACING) &&
             !pIDSA->get(DocumentSettingId::USE_FORMER_OBJECT_POS) &&
              pIDSA->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
             _rFrame.IsInDocBody() && !_rFrame.FindNextCnt() )
        {
            SwTextNode const& rNode(*rFormatAnchor.GetAnchorNode()->GetTextNode());
            assert(FrameContainsNode(_rFrame, rNode.GetIndex()));
            sal_Int32 const nObjAnchorPos(rFormatAnchor.GetAnchorContentOffset());
            const sal_Unicode cAnchorChar = nObjAnchorPos < rNode.Len()
                ? rNode.GetText()[nObjAnchorPos]
                : 0;
            if (cAnchorChar == CH_TXTATR_BREAKWORD)
            {
                const SwTextAttr* const pHint(
                    rNode.GetTextAttrForCharAt(nObjAnchorPos, RES_TXTATR_FLYCNT));
                if ( pHint )
                {
                    const SwFrameFormat* pFrameFormat =
                        static_cast<const SwTextFlyCnt*>(pHint)->GetFlyCnt().GetFrameFormat();
                    if ( pFrameFormat->Which() == RES_FLYFRMFMT )
                    {
                        SwNodeIndex nContentIndex = *(pFrameFormat->GetContent().GetContentIdx());
                        ++nContentIndex;
                        if ( nContentIndex.GetNode().IsNoTextNode() )
                        {
                            bRet = false;
                            // set needed data structure values for object positioning
                            SwRectFnSet aRectFnSet(&_rFrame);
                            SwRect aLastCharRect( _rFrame.getFrameArea() );
                            aRectFnSet.SetWidth( aLastCharRect, 1 );
                            _pAnchoredObj->maLastCharRect = aLastCharRect;
                            _pAnchoredObj->mnLastTopOfLine = aRectFnSet.GetTop(aLastCharRect);
                        }
                    }
                }
            }
        }
    }

    return bRet;
}

/**
 * Hide/show objects
 *
 * Method hides respectively shows objects, which are anchored at paragraph,
 * at/as a character of the paragraph, corresponding to the paragraph and
 * paragraph portion visibility.
 *
 * - is called from HideHidden() - should hide objects in hidden paragraphs and
 * - from Format_() - should hide/show objects in partly visible paragraphs
 */

void SwTextFrame::HideAndShowObjects()
{
    if ( GetDrawObjs() )
    {
        if ( IsHiddenNow() )
        {
            // complete paragraph is hidden. Thus, hide all objects
            for (SwAnchoredObject* i : *GetDrawObjs())
            {
                SdrObject* pObj = i->DrawObj();
                SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
                // under certain conditions
                const RndStdIds eAnchorType( pContact->GetAnchorId() );
                if ((eAnchorType != RndStdIds::FLY_AT_CHAR) ||
                    sw_HideObj(*this, eAnchorType, pContact->GetAnchorFormat(),
                                 i ))
                {
                    pContact->MoveObjToInvisibleLayer( pObj );
                }
            }
        }
        else
        {
            // paragraph is visible, but can contain hidden text portion.
            // first we check if objects are allowed to be hidden:
            const SwViewShell* pVsh = getRootFrame()->GetCurrShell();
            const bool bShouldBeHidden = !pVsh || !pVsh->GetWin() ||
                                         !pVsh->GetViewOptions()->IsShowHiddenChar();

            // Thus, show all objects, which are anchored at paragraph and
            // hide/show objects, which are anchored at/as character, according
            // to the visibility of the anchor character.
            for (SwAnchoredObject* i : *GetDrawObjs())
            {
                SdrObject* pObj = i->DrawObj();
                SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
                // Determine anchor type only once
                const RndStdIds eAnchorType( pContact->GetAnchorId() );

                if (eAnchorType == RndStdIds::FLY_AT_PARA)
                {
                    pContact->MoveObjToVisibleLayer( pObj );
                }
                else if ((eAnchorType == RndStdIds::FLY_AT_CHAR) ||
                         (eAnchorType == RndStdIds::FLY_AS_CHAR))
                {
                    sal_Int32 nHiddenStart;
                    sal_Int32 nHiddenEnd;
                    const SwFormatAnchor& rAnchorFormat = pContact->GetAnchorFormat();
                    const SwNode* pNode = rAnchorFormat.GetAnchorNode();
                    // When the object was already removed from text, but the layout hasn't been
                    // updated yet, this can be nullptr:
                    if (!pNode)
                        continue;
                    SwScriptInfo::GetBoundsOfHiddenRange(
                        *pNode->GetTextNode(),
                        rAnchorFormat.GetAnchorContentOffset(), nHiddenStart, nHiddenEnd);
                    // Under certain conditions
                    if ( nHiddenStart != COMPLETE_STRING && bShouldBeHidden &&
                        sw_HideObj(*this, eAnchorType, rAnchorFormat, i))
                    {
                        pContact->MoveObjToInvisibleLayer( pObj );
                    }
                    else
                        pContact->MoveObjToVisibleLayer( pObj );
                }
                else
                {
                    OSL_FAIL( " - object not anchored at/inside paragraph!?" );
                }
            }
        }
    }

    if (IsFollow())
    {
        SwTextFrame *pMaster = FindMaster();
        OSL_ENSURE(pMaster, "SwTextFrame without master");
        if (pMaster)
            pMaster->HideAndShowObjects();
    }
}

void SwLayoutFrame::HideAndShowObjects()
{
    for (SwFrame * pLower = Lower(); pLower; pLower = pLower->GetNext())
    {
        pLower->HideAndShowObjects();
    }
}

void SwFrame::HideAndShowObjects()
{
}

/**
 * Returns the first possible break point in the current line.
 * This method is used in SwTextFrame::Format() to decide whether the previous
 * line has to be formatted as well.
 * nFound is <= nEndLine.
 */

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=88 G=90

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge