/* -*- 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 <memory>
#include <utility>
#include <hintids.hxx>
#include <comphelper/string.hxx>
#include <svl/itemiter.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/formatbreakitem.hxx>
#include <svx/svdobj.hxx>
#include <osl/diagnose.h>
#include <crsrsh.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <pagefrm.hxx>
#include <cntfrm.hxx>
#include <rootfrm.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <fldbas.hxx>
#include <swtable.hxx>
#include <docary.hxx>
#include <txtfld.hxx>
#include <fmtfld.hxx>
#include <txtftn.hxx>
#include <txtinet.hxx>
#include <fmtinfmt.hxx>
#include <txttxmrk.hxx>
#include <frmfmt.hxx>
#include <flyfrm.hxx>
#include <viscrs.hxx>
#include "callnk.hxx"
#include <doctxm.hxx>
#include <docfld.hxx>
#include <expfld.hxx>
#include <reffld.hxx>
#include <flddat.hxx>
#include <cellatr.hxx>
#include <swundo.hxx>
#include <redline.hxx>
#include <fmtcntnt.hxx>
#include <fmthdft.hxx>
#include <pagedesc.hxx>
#include <fesh.hxx>
#include <charfmt.hxx>
#include <fmturl.hxx>
#include <txtfrm.hxx>
#include <wrong.hxx>
#include <calbck.hxx>
#include <unotools/intlwrapper.hxx>
#include <docufld.hxx>
#include <svx/srchdlg.hxx>
#include <frameformats.hxx>
#include <docsh.hxx>
#include <wrtsh.hxx>
#include <textcontentcontrol.hxx>

using namespace ::com::sun::star;

void SwCursorShell::MoveCursorToNum()
{
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );
    if( ActionPend() )
        return;
    CurrShell aCurr( this );
    // try to set cursor onto this position, at half of the char-
    // SRectangle's height
    Point aPt( m_pCurrentCursor->GetPtPos() );
    std::pair<Point, bool> const tmp(aPt, true);
    SwContentFrame * pFrame = m_pCurrentCursor->GetPointContentNode()->getLayoutFrame(
                GetLayout(), m_pCurrentCursor->GetPoint(), &tmp);
    pFrame->GetCharRect( m_aCharRect, *m_pCurrentCursor->GetPoint() );
    pFrame->Calc(GetOut());
    if( pFrame->IsVertical() )
    {
        aPt.setX(m_aCharRect.Center().getX());
        aPt.setY(pFrame->getFrameArea().Top() + GetUpDownX());
    }
    else
    {
        aPt.setY(m_aCharRect.Center().getY());
        aPt.setX(pFrame->getFrameArea().Left() + GetUpDownX());
    }
    pFrame->GetModelPositionForViewPoint( m_pCurrentCursor->GetPoint(), aPt );
    if ( !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
                                SwCursorSelOverFlags::ChangePos ))
    {
        UpdateCursor(SwCursorShell::UPDOWN |
                SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
                SwCursorShell::READONLY );
    }
}

/// go to next/previous point on the same level
void SwCursorShell::GotoNextNum()
{
    if (!SwDoc::GotoNextNum(*m_pCurrentCursor->GetPoint(), GetLayout()))
        return;
    MoveCursorToNum();
}

void SwCursorShell::GotoPrevNum()
{
    if (!SwDoc::GotoPrevNum(*m_pCurrentCursor->GetPoint(), GetLayout()))
        return;
    MoveCursorToNum();
}

/// jump from content to header
bool SwCursorShell::GotoHeaderText()
{
    const SwFrame* pFrame = GetCurrFrame()->FindPageFrame();
    while( pFrame && !pFrame->IsHeaderFrame() )
        pFrame = pFrame->GetLower();
    // found header, search 1. content frame
    while( pFrame && !pFrame->IsContentFrame() )
        pFrame = pFrame->GetLower();

    if( !pFrame )
        return false;

    CurrShell aCurr( this );
    // get header frame
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursor *pTmpCursor = getShellCursor( true );
    SwCursorSaveState aSaveState( *pTmpCursor );
    pFrame->Calc(GetOut());
    Point aPt( pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos() );
    pFrame->GetModelPositionForViewPoint( pTmpCursor->GetPoint(), aPt );
    if( !pTmpCursor->IsSelOvr() )
        UpdateCursor();
    else
        pFrame = nullptr;
    return nullptr != pFrame;
}

/// jump from content to footer
bool SwCursorShell::GotoFooterText()
{
    const SwPageFrame* pFrame = GetCurrFrame()->FindPageFrame();
    if( !pFrame )
        return false;

    const SwFrame* pLower = pFrame->GetLastLower();

    while( pLower && !pLower->IsFooterFrame() )
        pLower = pLower->GetLower();
    // found footer, search 1. content frame
    while( pLower && !pLower->IsContentFrame() )
        pLower = pLower->GetLower();

    if( !pLower )
        return false;

    SwCursor *pTmpCursor = getShellCursor( true );
    CurrShell aCurr( this );
    // get position in footer
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *pTmpCursor );
    pLower->Calc(GetOut());
    Point aPt( pLower->getFrameArea().Pos() + pLower->getFramePrintArea().Pos() );
    pLower->GetModelPositionForViewPoint( pTmpCursor->GetPoint(), aPt );
    if( !pTmpCursor->IsSelOvr() )
        UpdateCursor();
    else
        pFrame = nullptr;
    return nullptr != pFrame;
}

bool SwCursorShell::SetCursorInHdFt(size_t nDescNo, bool bInHeader, bool bEven, bool bFirst)
{
    SwDoc *pMyDoc = GetDoc();
    const SwPageDesc* pDesc = nullptr;

    CurrShell aCurr( this );

    if( SIZE_MAX == nDescNo )
    {
        // take the current one
        const SwContentFrame *pCurrFrame = GetCurrFrame();
        const SwPageFrame* pPage = (pCurrFrame == nullptr) ? nullptr : pCurrFrame->FindPageFrame();
        if( pPage && pMyDoc->ContainsPageDesc(
                pPage->GetPageDesc(), &nDescNo) )
            pDesc = pPage->GetPageDesc();
    }
    else
        if (nDescNo < pMyDoc->GetPageDescCnt())
            pDesc = &pMyDoc->GetPageDesc( nDescNo );

    if( !pDesc )
        return false;

    // check if the attribute exists
    const SwFormatContent* pCnt = nullptr;
    if( bInHeader )
    {
        const SwFormatHeader& rHd
            = bEven ? bFirst ? pDesc->GetFirstLeft().GetHeader() : pDesc->GetLeft().GetHeader()
                    : bFirst ? pDesc->GetFirstMaster().GetHeader() : pDesc->GetMaster().GetHeader();
        if( rHd.GetHeaderFormat() )
            pCnt = &rHd.GetHeaderFormat()->GetContent();
    }
    else
    {
        const SwFormatFooter& rFt
            = bEven ? bFirst ? pDesc->GetFirstLeft().GetFooter() : pDesc->GetLeft().GetFooter()
                    : bFirst ? pDesc->GetFirstMaster().GetFooter() : pDesc->GetMaster().GetFooter();
        if( rFt.GetFooterFormat() )
            pCnt = &rFt.GetFooterFormat()->GetContent();
    }

    if( !pCnt || !pCnt->GetContentIdx() )
        return false;

    SwNodeIndex aIdx( *pCnt->GetContentIdx(), 1 );
    SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
    if( !pCNd )
        pCNd = SwNodes::GoNext(&aIdx);

    Point aPt( m_pCurrentCursor->GetPtPos() );

    std::pair<Point, bool> const tmp(aPt, false);
    if (!pCNd || nullptr == pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp))
        return false;

    // then we can set the cursor in here
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    ClearMark();

    SwPosition& rPos = *m_pCurrentCursor->GetPoint();
    rPos.Assign( *pCNd );

    if (m_pCurrentCursor->IsSelOvr())
        return false;

    UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
                SwCursorShell::READONLY );
    return true;
}

/// jump to the next index
bool SwCursorShell::GotoNextTOXBase( const UIName* pName )
{
    const SwSectionFormats& rFormats = GetDoc()->GetSections();
    SwContentNode* pFnd = nullptr;
    for( SwSectionFormats::size_type n = rFormats.size(); n; )
    {
        const SwSection* pSect = rFormats[ --n ]->GetSection();
        if (SectionType::ToxContent == pSect->GetType())
        {
            SwSectionNode const*const pSectNd(
                    pSect->GetFormat()->GetSectionNode());
            if (   pSectNd
                && m_pCurrentCursor->GetPoint()->GetNode() < *pSectNd
                && (!pFnd  || pFnd->GetIndex() > pSectNd->GetIndex())
                && (!pName || *pName ==
                    static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()))
            {
                SwNodeIndex aIdx(*pSectNd, 1);
                SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
                if (!pCNd)
                    pCNd = SwNodes::GoNext(&aIdx);
                if (pCNd &&
                    pCNd->EndOfSectionIndex() <= pSectNd->EndOfSectionIndex())
                {
                    SwContentFrame const*const pCFrame(
                            pCNd->getLayoutFrame(GetLayout()));
                    if (pCFrame &&
                        (IsReadOnlyAvailable() || !pCFrame->IsProtected()))
                    {
                        pFnd = pCNd;
                    }
                }
            }
        }
    }
    if( !pFnd )
        return false;
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );
    m_pCurrentCursor->GetPoint()->Assign( *pFnd );
    bool bRet = !m_pCurrentCursor->IsSelOvr();
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return bRet;
}

/// jump to previous index
bool SwCursorShell::GotoPrevTOXBase( const UIName* pName )
{
    const SwSectionFormats& rFormats = GetDoc()->GetSections();
    SwContentNode* pFnd = nullptr;
    for( SwSectionFormats::size_type n = rFormats.size(); n; )
    {
        const SwSection* pSect = rFormats[ --n ]->GetSection();
        if (SectionType::ToxContent == pSect->GetType())
        {
            SwSectionNode const*const pSectNd(
                    pSect->GetFormat()->GetSectionNode());
            if (   pSectNd
                && m_pCurrentCursor->GetPoint()->GetNode() > *pSectNd->EndOfSectionNode()
                && (!pFnd  || *pFnd < *pSectNd)
                && (!pName || *pName ==
                    static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()))
            {
                SwNodeIndex aIdx(*pSectNd, 1);
                SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
                if (!pCNd)
                    pCNd = SwNodes::GoNext(&aIdx);
                if (pCNd &&
                    pCNd->EndOfSectionIndex() <= pSectNd->EndOfSectionIndex())
                {
                    SwContentFrame const*const pCFrame(
                            pCNd->getLayoutFrame(GetLayout()));
                    if (pCFrame &&
                        (IsReadOnlyAvailable() || !pCFrame->IsProtected()))
                    {
                        pFnd = pCNd;
                    }
                }
            }
        }
    }

    if( !pFnd )
        return false;

    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );
    m_pCurrentCursor->GetPoint()->Assign(*pFnd);
    bool bRet = !m_pCurrentCursor->IsSelOvr();
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return bRet;
}

/// jump to index of TOXMark
void SwCursorShell::GotoTOXMarkBase()
{
    SwTOXMarks aMarks;
    sal_uInt16 nCnt = SwDoc::GetCurTOXMark(*m_pCurrentCursor->GetPoint(), aMarks);
    if(!nCnt)
        return;
    // Take the 1. and get the index type. Ask it for the actual index.
    const SwTOXType* pType = aMarks[0]->GetTOXType();
    auto pContentFrame = pType->FindContentFrame(*GetLayout());
    if(!pContentFrame)
        return;
    SwCallLink aLk(*this); // watch Cursor-Moves
    SwCursorSaveState aSaveState(*m_pCurrentCursor);
    assert(pContentFrame->IsTextFrame());
    *m_pCurrentCursor->GetPoint() = static_cast<SwTextFrame const*>(pContentFrame)->MapViewToModelPos(TextFrameIndex(0));
    if(!m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr())
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
}

/// Jump to next/previous table formula
/// Optionally it is possible to also jump to broken formulas
bool SwCursorShell::GotoNxtPrvTableFormula( bool bNext, bool bOnlyErrors )
{
    SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );

    if( IsTableMode() )
        return false;

    bool bFnd = false;
    SwPosition aOldPos = *m_pCurrentCursor->GetPoint();
    SwPosition& rPos = *m_pCurrentCursor->GetPoint();

    Point aPt;
    SwPosition aFndPos( GetDoc()->GetNodes().GetEndOfContent() );
    if( !bNext )
        aFndPos.Assign(SwNodeOffset(0));
    SetGetExpField aFndGEF( aFndPos ), aCurGEF( rPos );

    {
        const SwNode* pSttNd = rPos.GetNode().FindTableBoxStartNode();
        if( pSttNd )
        {
            const SwTableBox* pTBox = pSttNd->FindTableNode()->GetTable().
                                        GetTableBox( pSttNd->GetIndex() );
            if( pTBox )
                aCurGEF = SetGetExpField( *pTBox );
        }
    }

    if( rPos.GetNode() < GetDoc()->GetNodes().GetEndOfExtras() )
    {
        // also at collection use only the first frame
        std::pair<Point, bool> const tmp(aPt, false);
        aCurGEF.SetBodyPos( *rPos.GetNode().GetContentNode()->getLayoutFrame( GetLayout(),
                                &rPos, &tmp) );
    }

    std::vector<SwTableBoxFormula*> aTableBoxFormulas;
    SwTable::GatherFormulas(*GetDoc(), aTableBoxFormulas);
    const sal_uInt32 nMaxItems(aTableBoxFormulas.size());
    if( nMaxItems > 0 )
    {
        sal_uInt8 nMaxDo = 2;
        do {
            for (SwTableBoxFormula* pItem : aTableBoxFormulas)
            {
                const SwTableBox* pTBox;
                auto & rFormulaItem = *pItem;
                pTBox = rFormulaItem.GetTableBox();
                if( pTBox &&
                    pTBox->GetSttNd() &&
                    pTBox->GetSttNd()->GetNodes().IsDocNodes() &&
                    ( !bOnlyErrors ||
                      !rFormulaItem.HasValidBoxes() ) )
                {
                    SwNodeIndex aIdx( *pTBox->GetSttNd() );
                    const SwContentNode* pCNd = SwNodes::GoNext(&aIdx);
                    std::pair<Point, bool> const tmp(aPt, false);
                    if (pCNd)
                    {
                        const SwContentFrame* pCFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                        if (pCFrame && (IsReadOnlyAvailable() || !pCFrame->IsProtected() ))
                        {
                            SetGetExpField aCmp( *pTBox );
                            aCmp.SetBodyPos( *pCFrame );

                            if( bNext ? ( aCurGEF < aCmp && aCmp < aFndGEF )
                                    : ( aCmp < aCurGEF && aFndGEF < aCmp ))
                            {
                                aFndGEF = aCmp;
                                bFnd = true;
                            }
                        }
                    }
                }
            }
            if( !bFnd )
            {
                if( bNext )
                {
                    rPos.Assign(SwNodeOffset(0), 0);
                    aCurGEF = SetGetExpField( rPos );
                    SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
                }
                else
                {
                    aCurGEF = SetGetExpField( SwPosition( GetDoc()->GetNodes().GetEndOfContent() ) );
                    SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
                }
            }
        } while( !bFnd && --nMaxDo );
    }

    if( !bFnd )
    {
        rPos = std::move(aOldPos);
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
        return false;
    }

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    aFndGEF.GetPosOfContent( rPos );
    m_pCurrentCursor->DeleteMark();

    bFnd = !m_pCurrentCursor->IsSelOvr();
    if( bFnd )
        UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
                    SwCursorShell::READONLY );

    return bFnd;
}

/// jump to next/previous index marker
bool SwCursorShell::GotoNxtPrvTOXMark( bool bNext )
{
    SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );

    if( IsTableMode() )
        return false;

    bool bFnd = false;
    SwPosition& rPos = *m_pCurrentCursor->GetPoint();

    Point aPt;
    SwPosition aFndPos( GetDoc()->GetNodes().GetEndOfContent() );
    if( !bNext )
        aFndPos.Assign(SwNodeOffset(0));
    SetGetExpField aFndGEF( aFndPos ), aCurGEF( rPos );

    if( rPos.GetNodeIndex() < GetDoc()->GetNodes().GetEndOfExtras().GetIndex() )
    {
        // also at collection use only the first frame
        std::pair<Point, bool> const tmp(aPt, false);
        aCurGEF.SetBodyPos( *rPos.GetNode().
                    GetContentNode()->getLayoutFrame(GetLayout(), &rPos, &tmp));
    }

    std::vector<const SwTOXMark*> aSurrogates;
    GetDoc()->ForEachTOXMark(
        [&aSurrogates] (const SwTOXMark& rItem) -> bool
        {
            aSurrogates.push_back(&rItem);
            return true;
        });
    const sal_uInt32 nMaxItems(aSurrogates.size());
    if( nMaxItems == 0 )
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
        return false;
    }

    const SwTextNode* pTextNd;
    const SwTextTOXMark* pTextTOX;
    do {
        for (const SwTOXMark* pItem : aSurrogates)
        {
            auto & rToxMarkItem = *pItem;
            pTextTOX = rToxMarkItem.GetTextTOXMark();
            if( !pTextTOX )
                continue;
            pTextNd = &pTextTOX->GetTextNode();
            if( !pTextNd->GetNodes().IsDocNodes() )
                continue;
            std::pair<Point, bool> const tmp(aPt, false);
            const SwContentFrame* pCFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
            if( pCFrame && ( IsReadOnlyAvailable() || !pCFrame->IsProtected() ))
            {
                SetGetExpField aCmp( *pTextNd, *pTextTOX );
                aCmp.SetBodyPos( *pCFrame );

                if( bNext ? ( aCurGEF < aCmp && aCmp < aFndGEF )
                          : ( aCmp < aCurGEF && aFndGEF < aCmp ))
                {
                    aFndGEF = aCmp;
                    bFnd = true;
                }
            }
        }
        if( !bFnd )
        {
            if ( bNext )
            {
                rPos.Assign(SwNodeOffset(0), 0);
                aCurGEF = SetGetExpField( rPos );
                SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
            }
            else
            {
                aCurGEF = SetGetExpField( SwPosition( GetDoc()->GetNodes().GetEndOfContent() ) );
                SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
            }
        }
    } while ( !bFnd );

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    aFndGEF.GetPosOfContent( rPos );

    bFnd = !m_pCurrentCursor->IsSelOvr();
    if( bFnd )
        UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
                    SwCursorShell::READONLY );
    return bFnd;
}

/// traveling between marks
const SwTOXMark& SwCursorShell::GotoTOXMark( const SwTOXMark& rStart,
                                            SwTOXSearch eDir )
{
    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    const SwTOXMark& rNewMark = GetDoc()->GotoTOXMark( rStart, eDir,
                                                    IsReadOnlyAvailable() );
    // set position
    SwPosition& rPos = *GetCursor()->GetPoint();
    rPos.Assign(rNewMark.GetTextTOXMark()->GetTextNode(),
                 rNewMark.GetTextTOXMark()->GetStart() );
    GetCursor()->DeleteMark(); // tdf#158783 prevent UpdateCursor resetting point

    if( !m_pCurrentCursor->IsSelOvr() )
        UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
                    SwCursorShell::READONLY );

    return rNewMark;
}

/// jump to next/previous field type
static void lcl_MakeFieldLst(
    SetGetExpFields& rLst,
    const SwFieldType& rFieldType,
    const bool bInReadOnly,
    const bool bChkInpFlag = false )
{
    // always search the 1. frame
    Point aPt;
    std::vector<SwFormatField*> vFields;
    rFieldType.GatherFields(vFields, false);
    for(SwFormatField* pFormatField: vFields)
    {
        SwTextField* pTextField = pFormatField->GetTextField();
        if ( pTextField != nullptr
             && ( !bChkInpFlag
                  || static_cast<const SwSetExpField*>(pTextField->GetFormatField().GetField())->GetInputFlag() ) )
        {
            const SwTextNode& rTextNode = pTextField->GetTextNode();
            std::pair<Point, bool> const tmp(aPt, false);
            const SwContentFrame* pCFrame =
                rTextNode.getLayoutFrame(
                    rTextNode.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
                    nullptr, &tmp);
            if ( pCFrame != nullptr
                 && ( bInReadOnly || !pCFrame->IsProtected() ) )
            {
                std::unique_ptr<SetGetExpField> pNew(new SetGetExpField( rTextNode, pTextField ));
                pNew->SetBodyPos( *pCFrame );
                rLst.insert( std::move(pNew) );
            }
        }
    }
}

static SetGetExpFields::const_iterator
lcl_FindField(bool & o_rFound, SetGetExpFields const& rSrtLst,
        SwRootFrame const *const pLayout, SwTextNode *const pTextNode,
        SwTextField const *const pTextField, SwPosition const& rPos,
        sal_Int32 const nContentOffset)
{
    std::optional<SetGetExpField> oSrch;
    if (-1 == nContentOffset)
    {
        oSrch.emplace(rPos.GetNode(), pTextField, rPos.GetContentIndex());
    }
    else
    {
        oSrch.emplace(rPos.GetNode(), pTextField, nContentOffset);
    }

    if (rPos.GetNodeIndex() < pTextNode->GetNodes().GetEndOfExtras().GetIndex())
    {
        // also at collection use only the first frame
        Point aPt;
        std::pair<Point, bool> const tmp(aPt, false);
        oSrch->SetBodyPos(*pTextNode->getLayoutFrame(pLayout, &rPos, &tmp));
    }

    SetGetExpFields::const_iterator it = rSrtLst.lower_bound(&*oSrch);

    o_rFound = (it != rSrtLst.end()) && (**it == *oSrch);
    return it;
}

bool SwCursorShell::MoveFieldType(
    const SwFieldType* pFieldType,
    const bool bNext,
    const SwFieldIds nResType,
    const bool bAddSetExpressionFieldsToInputFields )
{
    // sorted list of all fields
    SetGetExpFields aSrtLst;

    if ( pFieldType )
    {
        if( SwFieldIds::Input != pFieldType->Which() && !pFieldType->HasWriterListeners() )
        {
            return false;
        }

        // found Modify object, add all fields to array
        ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable() );

        if( SwFieldIds::Input == pFieldType->Which() && bAddSetExpressionFieldsToInputFields )
        {
            // there are hidden input fields in the set exp. fields
            const SwFieldTypes& rFieldTypes = *mxDoc->getIDocumentFieldsAccess().GetFieldTypes();
            const size_t nSize = rFieldTypes.size();
            for( size_t i=0; i < nSize; ++i )
            {
                pFieldType = rFieldTypes[ i ].get();
                if ( SwFieldIds::SetExp == pFieldType->Which() )
                {
                    ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable(), true );
                }
            }
        }
    }
    else
    {
        const SwFieldTypes& rFieldTypes = *mxDoc->getIDocumentFieldsAccess().GetFieldTypes();
        const size_t nSize = rFieldTypes.size();
        const bool bAllFieldTypes = nResType == SwFieldIds::Unknown;
        for( size_t i=0; i < nSize; ++i )
        {
            pFieldType = rFieldTypes[ i ].get();
            if (bAllFieldTypes || nResType == pFieldType->Which())
            {
                ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable() );
            }
        }
    }

    // found no fields?
    if( aSrtLst.empty() )
        return false;

    SetGetExpFields::const_iterator it;
    SwCursor* pCursor = getShellCursor( true );
    {
        // (1998): Always use field for search so that the right one is found as
        // well some are in frames that are anchored to a paragraph that has a
        // field
        const SwPosition& rPos = *pCursor->GetPoint();

        SwTextNode* pTNd = rPos.GetNode().GetTextNode();
        assert(pTNd && "No ContentNode");

        SwTextField * pTextField = pTNd->GetFieldTextAttrAt(rPos.GetContentIndex(), ::sw::GetTextAttrMode::Default);
        const bool bDelField = ( pTextField == nullptr );
        sal_Int32 nContentOffset = -1;

        if( bDelField )
        {
            // create dummy for the search
            // NOTE: with SfxPoolItemHolder in SwTextAttr the
            // SwFormatField will just be managed by it, when
            // wanted and handing over bPassingOwnership==true
            pTextField = new SwTextField (
                SfxPoolItemHolder(
                    mxDoc->GetAttrPool(),
                    new SwFormatField(
                        SwDateTimeField(
                            static_cast<SwDateTimeFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DateTime )))),
                    true), // bPassingOwnership
                rPos.GetContentIndex(),
                mxDoc->IsClipBoard() );
            pTextField->ChgTextNode( pTNd );
        }
        else
        {
            // the cursor might be anywhere inside the input field,
            // but we will be searching for the field start
            if (pTextField->Which() == RES_TXTATR_INPUTFIELD
                    && rPos.GetContentIndex() != pTextField->GetStart())
                nContentOffset = pTextField->GetStart();
        }
        bool isSrch;
        it = lcl_FindField(isSrch, aSrtLst,
                GetLayout(), pTNd, pTextField, rPos, nContentOffset);

        if( bDelField )
        {
            // with using SfxPoolItemHolder in SwTextAttr there is no need anymore
            // to cleanup the contained SwFormatField self
            delete pTextField;
        }

        if( it != aSrtLst.end() && isSrch ) // found
        {
            if( bNext )
            {
                if( ++it == aSrtLst.end() )
                    return false; // already at the end
            }
            else
            {
                if( it == aSrtLst.begin() )
                    return false; // no more steps backward possible
                --it;
            }
        }
        else // not found
        {
            if( bNext )
            {
                if( it == aSrtLst.end() )
                    return false;
            }
            else
            {
                if( it == aSrtLst.begin() )
                    return false; // no more steps backward possible
                --it;
            }
        }
    }
    const SetGetExpField& rFnd = **it;

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *pCursor );

    rFnd.GetPosOfContent( *pCursor->GetPoint() );
    bool bRet = !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
                                     SwCursorSelOverFlags::Toggle );
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return bRet;
}

bool SwCursorShell::GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote)
{
    if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
        pWrtSh->addCurrentPosition();

    bool bRet = false;
    SwCursor* pCursor = getShellCursor(true);

    CurrShell aCurr(this);
    SwCallLink aLk(*this); // watch Cursor-Moves
    SwCursorSaveState aSaveState(*pCursor);

    pCursor->GetPoint()->Assign(rTextFootnote.GetTextNode(),
                                rTextFootnote.GetStart());
    bRet = !pCursor->IsSelOvr();
    if (bRet)
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return bRet;
}

bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& rContentControl)
{
    const std::shared_ptr<SwContentControl>& pContentControl = rContentControl.GetContentControl();
    const SwTextContentControl* pTextContentControl = pContentControl->GetTextAttr();
    if (!pTextContentControl)
        return false;

    CurrShell aCurr(this);
    SwCallLink aLink(*this);

    SwCursor* pCursor = getShellCursor(true);
    SwCursorSaveState aSaveState(*pCursor);

    SwTextNode* pTextNode = pContentControl->GetTextNode();
    // Don't select the text attribute itself at the start.
    sal_Int32 nStart = pTextContentControl->GetStart() + 1;
    pCursor->GetPoint()->Assign(*pTextNode, nStart);

    bool bRet = true;
    // select contents for certain controls or conditions
    if (pContentControl->GetShowingPlaceHolder() || pContentControl->GetCheckbox()
        || pContentControl->GetSelectedListItem() || pContentControl->GetSelectedDate())
    {
        pCursor->SetMark();
        // Don't select the CH_TXTATR_BREAKWORD itself at the end.
        sal_Int32 nEnd = *pTextContentControl->End() - 1;
        pCursor->GetMark()->Assign(*pTextNode, nEnd);
        bRet = !pCursor->IsSelOvr();
    }
    else
        ClearMark();

    if (bRet)
    {
        UpdateCursor(SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE
                     | SwCursorShell::READONLY);
    }

    return bRet;
}

/**
 * Go to the next (or previous) form control, based first on tabIndex and then paragraph position,
 * where a tabIndex of 1 is first, 0 is last, and -1 is excluded.
 */
void SwCursorShell::GotoFormControl(bool bNext)
{
    // (note: this only applies to modern content controls and legacy fieldmarks,
    //  since activeX richText controls aren't exposed to SW keystrokes)

    struct FormControlSort
    {
        bool operator()(std::pair<const SwPosition&, sal_uInt32> rLHS,
                        std::pair<const SwPosition&, sal_uInt32> rRHS) const
        {
            assert(rLHS.second && rRHS.second && "tabIndex zero must be changed to SAL_MAX_UINT32");
            //first compare tabIndexes where 1 has the priority.
            if (rLHS.second < rRHS.second)
                return true;
            if (rLHS.second > rRHS.second)
                return false;

            // when tabIndexes are equal (and they usually are) then sort by paragraph position
            return rLHS.first < rRHS.first;
        }
    };
    std::map<std::pair<SwPosition, sal_uInt32>,
             std::pair<SwTextContentControl*, sw::mark::Fieldmark*>, FormControlSort>  aFormMap;

    // add all of the eligible modern Content Controls into a sorted map
    SwContentControlManager& rManager = GetDoc()->GetContentControlManager();
    for (size_t  i = 0; i < rManager.GetCount(); ++i)
    {
        SwTextContentControl* pTCC = rManager.UnsortedGet(i);
        if (!pTCC || !pTCC->GetTextNode())
            continue;
        auto pCC = pTCC->GetContentControl().GetContentControl();

        // -1 indicates the control should not participate in keyboard tab navigation
        if (pCC && pCC->GetTabIndex() == SAL_MAX_UINT32)
            continue;

        const SwPosition nPos(*pTCC->GetTextNode(), pTCC->GetStart());

        // since 0 is the lowest priority (1 is the highest), and -1 has already been excluded,
        // use SAL_MAX_UINT32 as zero's tabIndex so that automatic sorting is correct.
        sal_uInt32 nTabIndex = pCC && pCC->GetTabIndex() ? pCC->GetTabIndex() : SAL_MAX_UINT32;

        const std::pair<SwTextContentControl*, sw::mark::Fieldmark*> pFormControl(pTCC, nullptr);
        aFormMap[std::make_pair(nPos, nTabIndex)] = pFormControl;
    }

    if (aFormMap.begin() == aFormMap.end())
    {
        // only legacy fields exist. Avoid reprocessing everything and use legacy code path.
        GotoFieldmark(bNext ? GetFieldmarkAfter() : GetFieldmarkBefore());
        return;
    }

    // add all of the legacy form field controls into the sorted map
    IDocumentMarkAccess* pMarkAccess = GetDoc()->getIDocumentMarkAccess();
    for (auto it = pMarkAccess->getFieldmarksBegin(); it != pMarkAccess->getFieldmarksEnd(); ++it)
    {
        sw::mark::Fieldmark* pFieldMark = *it;
        assert(pFieldMark);
        // legacy form fields do not have (functional) tabIndexes - use lowest priority for them
        aFormMap[std::make_pair((*it)->GetMarkStart(), SAL_MAX_UINT32)] =
            std::pair<SwTextContentControl*, sw::mark::Fieldmark*>(nullptr, pFieldMark);
    }

    if (aFormMap.begin() == aFormMap.end())
        return;

    // Identify the current location in the document, and the current tab index priority

    // A content control could contain a Fieldmark, so check for legacy fieldmarks first
    sw::mark::Fieldmark* pFieldMark = GetCurrentFieldmark();
    SwTextContentControl* pTCC = !pFieldMark ? CursorInsideContentControl() : nullptr;

    auto pCC = pTCC ? pTCC->GetContentControl().GetContentControl() : nullptr;
    const sal_uInt32 nCurTabIndex = pCC && pCC->GetTabIndex() ? pCC->GetTabIndex() : SAL_MAX_UINT32;

    SwPosition nCurPos(*GetCursor()->GetPoint());
    if (pFieldMark)
        nCurPos = pFieldMark->GetMarkStart();
    else if (pTCC && pTCC->GetTextNode())
        nCurPos = SwPosition(*pTCC->GetTextNode(), pTCC->GetStart());

    // Find the previous (or next) tab control and navigate to it
    const std::pair<SwPosition, sal_uInt32> nOldPos(nCurPos, nCurTabIndex);

    // lower_bound acts like find, and returns a pointer to nFindPos if it exists,
    // otherwise it will point to the previous entry.
    auto aNewPos = aFormMap.lower_bound(nOldPos);
    if (bNext && aNewPos != aFormMap.end())
        ++aNewPos;
    else if (!bNext && aNewPos != aFormMap.end() && aNewPos->first == nOldPos)
    {
        // Found the current position - need to return previous
        if (aNewPos == aFormMap.begin())
            aNewPos = aFormMap.end(); // prepare to loop around
        else
            --aNewPos;
    }

    if (aNewPos == aFormMap.end())
    {
        // Loop around to the other side
        if (bNext)
            aNewPos = aFormMap.begin();
        else
            --aNewPos;
    }

    // the entry contains a pointer to either a Content Control (first) or Fieldmark (second)
    if (aNewPos->second.first)
    {
        auto& rFCC = static_cast<SwFormatContentControl&>(aNewPos->second.first->GetAttr());
        GotoFormatContentControl(rFCC);
    }
    else
    {
        assert(aNewPos->second.second);
        GotoFieldmark(aNewPos->second.second);
    }
}

bool SwCursorShell::GotoFormatField( const SwFormatField& rField )
{
    SwTextField const*const pTextField(rField.GetTextField());
    if (!pTextField
        || (GetLayout()->IsHideRedlines()
             && sw::IsFieldDeletedInModel(
                 GetDoc()->getIDocumentRedlineAccess(), *pTextField)))
        return false;

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves

    SwCursor* pCursor = getShellCursor( true );
    SwCursorSaveState aSaveState( *pCursor );

    SwTextNode* pTNd = pTextField->GetpTextNode();
    pCursor->GetPoint()->Assign(*pTNd, pTextField->GetStart() );

    bool bRet = !pCursor->IsSelOvr();
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    if (&pCursor->GetPoint()->GetNode() != pTNd)
    {
        // tdf#161346 failed to move to field
        return false;
    }
    return bRet;
}

SwTextField * SwCursorShell::GetTextFieldAtPos(
    const SwPosition* pPos,
    ::sw::GetTextAttrMode const eMode)
{
    SwTextField* pTextField = nullptr;

    SwTextNode * const pNode = pPos->GetNode().GetTextNode();
    if ( pNode != nullptr )
    {
        pTextField = pNode->GetFieldTextAttrAt(pPos->GetContentIndex(), eMode);
    }

    return pTextField;
}

SwTextField* SwCursorShell::GetTextFieldAtCursor(
    const SwPaM* pCursor,
    ::sw::GetTextAttrMode const eMode)
{
    SwTextField* pTextField = GetTextFieldAtPos(pCursor->Start(), eMode);
    if ( !pTextField
        || pCursor->Start()->GetNode() != pCursor->End()->GetNode() )
        return nullptr;

    SwTextField* pFieldAtCursor = nullptr;
    const sal_Int32 nTextFieldLength =
        pTextField->End() != nullptr
        ? *(pTextField->End()) - pTextField->GetStart()
        : 1;
    if ( ( pCursor->End()->GetContentIndex() - pCursor->Start()->GetContentIndex() ) <= nTextFieldLength )
    {
        pFieldAtCursor = pTextField;
    }

    return pFieldAtCursor;
}

SwField* SwCursorShell::GetFieldAtCursor(
    const SwPaM *const pCursor,
    const bool bIncludeInputFieldAtStart)
{
    SwTextField *const pField(GetTextFieldAtCursor(pCursor,
        bIncludeInputFieldAtStart ? ::sw::GetTextAttrMode::Default : ::sw::GetTextAttrMode::Expand));
    return pField
        ? const_cast<SwField*>(pField->GetFormatField().GetField())
        : nullptr;
}

SwField* SwCursorShell::GetCurField( const bool bIncludeInputFieldAtStart ) const
{
    SwPaM* pCursor = GetCursor();
    if ( pCursor->IsMultiSelection() )
    {
        // multi selection not handled.
        return nullptr;
    }

    SwField* pCurField = GetFieldAtCursor( pCursor, bIncludeInputFieldAtStart );
    if ( pCurField != nullptr
         && SwFieldIds::Table == pCurField->GetTyp()->Which() )
    {
        // table formula? convert internal name into external
        const SwTableNode* pTableNd = IsCursorInTable();
        static_cast<SwTableField*>(pCurField)->PtrToBoxNm( pTableNd ? &pTableNd->GetTable() : nullptr );
    }

    return pCurField;
}

bool SwCursorShell::CursorInsideInputField() const
{
    for(SwPaM& rCursor : GetCursor()->GetRingContainer())
    {
        if (dynamic_cast<const SwTextInputField*>(GetTextFieldAtCursor(&rCursor, ::sw::GetTextAttrMode::Parent)))
            return true;
    }
    return false;
}

SwTextContentControl* SwCursorShell::CursorInsideContentControl() const
{
    for (SwPaM& rCursor : GetCursor()->GetRingContainer())
    {
        const SwPosition* pStart = rCursor.Start();
        SwTextNode* pTextNode = pStart->GetNode().GetTextNode();
        if (!pTextNode)
        {
            continue;
        }

        sal_Int32 nIndex = pStart->GetContentIndex();
        if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent))
        {
            return static_txtattr_cast<SwTextContentControl*>(pAttr);
        }
    }

    return nullptr;
}

bool SwCursorShell::PosInsideInputField( const SwPosition& rPos )
{
    return dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Parent)) != nullptr;
}

bool SwCursorShell::DocPtInsideInputField( const Point& rDocPt ) const
{
    SwPosition aPos( *(GetCursor()->Start()) );
    Point aDocPt( rDocPt );
    if ( GetLayout()->GetModelPositionForViewPoint( &aPos, aDocPt ) )
    {
        return PosInsideInputField( aPos );
    }
    return false;
}

sal_Int32 SwCursorShell::StartOfInputFieldAtPos( const SwPosition& rPos )
{
    const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Default));
    assert(pTextInputField != nullptr
        && "<SwEditShell::StartOfInputFieldAtPos(..)> - no Input Field at given position");
    return pTextInputField->GetStart();
}

sal_Int32 SwCursorShell::EndOfInputFieldAtPos( const SwPosition& rPos )
{
    const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Default));
    assert(pTextInputField != nullptr
        && "<SwEditShell::EndOfInputFieldAtPos(..)> - no Input Field at given position");
    return *(pTextInputField->End());
}

void SwCursorShell::GotoOutline( SwOutlineNodes::size_type nIdx )
{
    SwCursor* pCursor = getShellCursor( true );

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *pCursor );

    const SwNodes& rNds = GetDoc()->GetNodes();
    SwTextNode* pTextNd = rNds.GetOutLineNds()[ nIdx ]->GetTextNode();
    pCursor->GetPoint()->Assign(*pTextNd);

    if( !pCursor->IsSelOvr() )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
}

bool SwCursorShell::GotoOutline( const OUString& rName )
{
    SwCursor* pCursor = getShellCursor( true );

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *pCursor );

    bool bRet = false;
    if (mxDoc->GotoOutline(*pCursor->GetPoint(), rName, GetLayout())
        && !pCursor->IsSelOvr())
    {
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
        bRet = true;
    }
    return bRet;
}

/// jump to next node with outline num.
bool SwCursorShell::GotoNextOutline()
{
    const SwNodes& rNds = GetDoc()->GetNodes();

    if ( rNds.GetOutLineNds().empty() )
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
        return false;
    }

    SwCursor* pCursor = getShellCursor( true );
    SwNode* pNd = &(pCursor->GetPointNode());
    SwOutlineNodes::size_type nPos;
    bool bUseFirst = !rNds.GetOutLineNds().Seek_Entry( pNd, &nPos );
    SwOutlineNodes::size_type const nStartPos(nPos);

    do
    {
        if (!bUseFirst)
        {
            ++nPos;
        }
        if (rNds.GetOutLineNds().size() <= nPos)
        {
            nPos = 0;
        }

        if (bUseFirst)
        {
            bUseFirst = false;
        }
        else
        {
            if (nPos == nStartPos)
            {
                SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
                return false;
            }
        }

        pNd = rNds.GetOutLineNds()[ nPos ];
    }
    while (!sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode()));

    if (nPos < nStartPos)
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
    }
    else
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
    }

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *pCursor );
    pCursor->GetPoint()->Assign(*pNd);

    bool bRet = !pCursor->IsSelOvr();
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return bRet;
}

/// jump to previous node with outline num.
bool SwCursorShell::GotoPrevOutline()
{
    const SwNodes& rNds = GetDoc()->GetNodes();

    if ( rNds.GetOutLineNds().empty() )
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
        return false;
    }

    SwCursor* pCursor = getShellCursor( true );
    SwNode* pNd = &(pCursor->GetPointNode());
    SwOutlineNodes::size_type nPos;
    (void)rNds.GetOutLineNds().Seek_Entry(pNd, &nPos);
    SwOutlineNodes::size_type const nStartPos(nPos);

    do
    {
        if (nPos == 0)
        {
            nPos = rNds.GetOutLineNds().size() - 1;
        }
        else
        {
            --nPos; // before
        }
        if (nPos == nStartPos)
        {
            pNd = nullptr;
            break;
        }

        pNd = rNds.GetOutLineNds()[ nPos ];
    }
    while (!sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode()));

    if (!pNd)
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
        return false;
    }

    if (nStartPos < nPos)
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
    }
    else
    {
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
    }
    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *pCursor );
    pCursor->GetPoint()->Assign(*pNd);

    bool bRet = !pCursor->IsSelOvr();
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return bRet;
}

/// search "outline position" before previous outline node at given level
SwOutlineNodes::size_type SwCursorShell::GetOutlinePos(sal_uInt8 nLevel, SwPaM* pPaM)
{
    SwPaM* pCursor = pPaM ? pPaM : getShellCursor(true);
    const SwNodes& rNds = GetDoc()->GetNodes();

    SwNode* pNd = &(pCursor->GetPointNode());
    SwOutlineNodes::size_type nPos;
    if( rNds.GetOutLineNds().Seek_Entry( pNd, &nPos ))
        nPos++; // is at correct position; take next for while

    while( nPos-- ) // check the one in front of the current
    {
        pNd = rNds.GetOutLineNds()[ nPos ];

        if (sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode())
            && pNd->GetTextNode()->GetAttrOutlineLevel()-1 <= nLevel)
        {
            if (pNd->GetIndex() < rNds.GetEndOfExtras().GetIndex()
                    && pCursor->GetPointNode().GetIndex() > rNds.GetEndOfExtras().GetIndex())
            {
                // node found in extras but cursor position is not in extras
                return SwOutlineNodes::npos;
            }
            return nPos;
        }
    }
    return SwOutlineNodes::npos; // no more left
}

void SwCursorShell::MakeOutlineSel(SwOutlineNodes::size_type nSttPos, SwOutlineNodes::size_type nEndPos,
                                  bool bWithChildren , bool bKillPams, const SwOutlineNodesInline* pOutlNdsInline)
{
    const SwNodes& rNds = GetDoc()->GetNodes();
    const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
    if( rOutlNds.empty() )
        return;

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves

    if( nSttPos > nEndPos ) // parameters switched?
    {
        OSL_ENSURE( false, "Start > End for array access" );
        std::swap(nSttPos, nEndPos);
    }

    SwNode* pSttNd = rOutlNds[ nSttPos ];
    SwNode* pEndNd = rOutlNds[ nEndPos ];

    if (pOutlNdsInline)
    {
        pSttNd = const_cast<SwNode*>(SwOutlineNodes::GetRootNode(pSttNd));

        SwOutlineNodesInline::size_type nEndPosInline;
        pOutlNdsInline->Seek_Entry( pEndNd, &nEndPosInline );
        assert(nEndPosInline != SwOutlineNodesInline::npos && "always sets some number <= pOutlNdsInline->size()");

        // headings in flys
        if (bWithChildren)
        {
            const int nLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel() - 1;
            for( ++nEndPosInline; nEndPosInline < pOutlNdsInline->size(); ++nEndPosInline )
            {
                pEndNd = (*pOutlNdsInline)[ nEndPosInline ];
                const int nNxtLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel()-1;
                if( nNxtLevel <= nLevel )
                    break; // EndPos is now on the next one
            }
            // set anchor node of the fly node
            if ( nEndPosInline < pOutlNdsInline->size() )
                pEndNd = const_cast<SwNode*>(SwOutlineNodes::GetRootNode(pEndNd));
        }
        else if (++nEndPosInline < pOutlNdsInline->size())
            pEndNd = const_cast<SwNode*>(SwOutlineNodes::GetRootNode((*pOutlNdsInline)[nEndPosInline]));

        if (nEndPosInline == pOutlNdsInline->size()) // no end found
            pEndNd = &rNds.GetEndOfContent();
    }
    else
    {
        if (bWithChildren)
        {
            const int nLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel()-1;
            for( ++nEndPos; nEndPos < rOutlNds.size(); ++nEndPos )
            {
                pEndNd = rOutlNds[ nEndPos ];
                const int nNxtLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel()-1;
                if( nNxtLevel <= nLevel )
                    break; // EndPos is now on the next one
            }
        }
        // if without children then set onto next one
        else if (++nEndPos < rOutlNds.size())
            pEndNd = rOutlNds[ nEndPos ];

        if (nEndPos == rOutlNds.size()) // no end found
            pEndNd = &rNds.GetEndOfContent();
    }

    if( bKillPams )
        KillPams();

    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    // set end to the end of the previous content node
    m_pCurrentCursor->GetPoint()->Assign(*pSttNd);
    m_pCurrentCursor->SetMark();
    m_pCurrentCursor->GetPoint()->Assign(*pEndNd);
    m_pCurrentCursor->Move( fnMoveBackward, GoInNode ); // end of predecessor

    // and everything is already selected
    bool bRet = !m_pCurrentCursor->IsSelOvr();
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
}

/// jump to reference marker
bool SwCursorShell::GotoRefMark( const SwMarkName& rRefMark, ReferencesSubtype nSubType,
                                    sal_uInt16 nSeqNo, sal_uInt16 nFlags )
{
    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    sal_Int32 nPos = -1;

    SwPaM* pCursor = GetCursor();
    SwPosition* pPos = pCursor->GetPoint();
    SwTextNode* pRefTextNd = pPos->GetNode().GetTextNode();
    SwContentFrame* pRefFrame = GetCurrFrame();

    SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor(GetDoc(), rRefMark,
                                nSubType, nSeqNo, nFlags, &nPos, nullptr, GetLayout(), pRefTextNd, pRefFrame);
    if( !pTextNd || !pTextNd->GetNodes().IsDocNodes() )
        return false;

    m_pCurrentCursor->GetPoint()->Assign(*pTextNd, nPos );

    if( m_pCurrentCursor->IsSelOvr() )
        return false;

    UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return true;
}

bool SwCursorShell::IsPageAtPos( const Point &rPt ) const
{
    if( GetLayout() )
        return nullptr != GetLayout()->GetPageAtPos( rPt );
    return false;
}

bool SwCursorShell::GetContentAtPos( const Point& rPt,
                                   SwContentAtPos& rContentAtPos,
                                   bool bSetCursor,
                                   SwRect* pFieldRect )
{
    CurrShell aCurr( this );
    bool bRet = false;

    if( IsTableMode() )
    {
        rContentAtPos.eContentAtPos = IsAttrAtPos::NONE;
        rContentAtPos.aFnd.pField = nullptr;
        return false;
    }

    Point aPt( rPt );
    SwPosition aPos( *m_pCurrentCursor->GetPoint() );

    SwTextNode* pTextNd;
    SwCursorMoveState aTmpState;
    aTmpState.m_bFieldInfo = true;
    aTmpState.m_bExactOnly = !( IsAttrAtPos::Outline & rContentAtPos.eContentAtPos );
    aTmpState.m_bContentCheck = bool(IsAttrAtPos::ContentCheck & rContentAtPos.eContentAtPos);
    aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
    aTmpState.m_bPosMatchesBounds = true; // treat last half of character same as first half

    SwSpecialPos aSpecialPos;
    aTmpState.m_pSpecialPos = ( IsAttrAtPos::SmartTag & rContentAtPos.eContentAtPos ) ?
                            &aSpecialPos : nullptr;

    const bool bCursorFoundExact = GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState );
    pTextNd = aPos.GetNode().GetTextNode();

    const SwNodes& rNds = GetDoc()->GetNodes();
    if( pTextNd
        && IsAttrAtPos::Outline & rContentAtPos.eContentAtPos
        && !rNds.GetOutLineNds().empty() )
    {
        // only for nodes in outline nodes
        SwOutlineNodes::size_type nPos = 0;
        bool bFoundOutline = rNds.GetOutLineNds().Seek_Entry(pTextNd, &nPos);
        if (!bFoundOutline && nPos && (IsAttrAtPos::AllowContaining & rContentAtPos.eContentAtPos))
        {
            // nPos points to the first found outline node not before pTextNd, or to end();
            // when bFoundOutline is false, and nPos is not 0, it means that there were
            // outline nodes before pTextNd, and nPos-1 points to the last of those.
            pTextNd = rNds.GetOutLineNds()[nPos - 1]->GetTextNode();
            bFoundOutline = true;
        }
        if (bFoundOutline)
        {
            rContentAtPos.eContentAtPos = IsAttrAtPos::Outline;
            rContentAtPos.sStr = sw::GetExpandTextMerged(GetLayout(), *pTextNd, true, false, ExpandMode::ExpandFootnote);
            rContentAtPos.aFnd.pNode = pTextNd;
            bRet = true;
        }
    }
    else if ( IsAttrAtPos::ContentCheck & rContentAtPos.eContentAtPos
              && bCursorFoundExact )
    {
        bRet = true;
    }
    else if( pTextNd
             && IsAttrAtPos::NumLabel & rContentAtPos.eContentAtPos)
    {
        bRet = aTmpState.m_bInNumPortion;
        rContentAtPos.aFnd.pNode = sw::GetParaPropsNode(*GetLayout(), aPos.GetNode());

        Size aSizeLogic(aTmpState.m_nInNumPortionOffset, 0);
        Size aSizePixel = GetWin()->LogicToPixel(aSizeLogic);
        rContentAtPos.nDist = aSizePixel.Width();
    }
    else if( bCursorFoundExact && pTextNd )
    {
        SwContentFrame *pFrame(nullptr);
        if( !aTmpState.m_bPosCorr )
        {
            SwTextAttr* pTextAttr;
            if ( IsAttrAtPos::SmartTag & rContentAtPos.eContentAtPos
                 && !aTmpState.m_bFootnoteNoInfo )
            {
                const SwWrongList* pSmartTagList = pTextNd->GetSmartTags();
                sal_Int32 nCurrent = aPos.GetContentIndex();
                const sal_Int32 nBegin = nCurrent;
                sal_Int32 nLen = 1;

                if (pSmartTagList && pSmartTagList->InWrongWord(nCurrent, nLen) && !pTextNd->IsSymbolAt(nBegin))
                {
                    const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin );
                    const SwWrongList* pSubList = pSmartTagList->SubList( nIndex );
                    if ( pSubList )
                    {
                        nCurrent = aTmpState.m_pSpecialPos->nCharOfst;

                        if ( pSubList->InWrongWord( nCurrent, nLen ) )
                            bRet = true;
                    }
                    else
                        bRet = true;

                    if( bRet && bSetCursor )
                    {
                        SwCursorSaveState aSaveState( *m_pCurrentCursor );
                        SwCallLink aLk( *this ); // watch Cursor-Moves
                        m_pCurrentCursor->DeleteMark();
                        *m_pCurrentCursor->GetPoint() = aPos;
                        if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection | SwCursorSelOverFlags::Toggle) )
                            bRet = false;
                        else
                            UpdateCursor();
                    }
                    if( bRet )
                    {
                        rContentAtPos.eContentAtPos = IsAttrAtPos::SmartTag;

                        std::pair<Point, bool> tmp(aPt, true);
                        if (pFieldRect)
                        {
                            pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                            if (pFrame)
                                pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
                        }
                    }
                }
            }

            if ( !bRet
                 && ( IsAttrAtPos::Field | IsAttrAtPos::ClickField ) & rContentAtPos.eContentAtPos
                 && !aTmpState.m_bFootnoteNoInfo )
            {
                pTextAttr = pTextNd->GetFieldTextAttrAt( aPos.GetContentIndex() );
                const SwField* pField = pTextAttr != nullptr
                                      ? pTextAttr->GetFormatField().GetField()
                                      : nullptr;
                if ( IsAttrAtPos::ClickField & rContentAtPos.eContentAtPos
                     && pField && !pField->HasClickHdl() )
                {
                    pField = nullptr;
                }

                if ( pField )
                {
                    if (pFieldRect)
                    {
                        std::pair<Point, bool> tmp(aPt, true);
                        pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                        if (pFrame)
                        {
                            //tdf#116397 now that we looking for the bounds of the field drop the SmartTag
                            //index within field setting so we don't the bounds of the char within the field
                            SwSpecialPos* pSpecialPos = aTmpState.m_pSpecialPos;
                            aTmpState.m_pSpecialPos = nullptr;
                            pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
                            aTmpState.m_pSpecialPos = pSpecialPos;
                        }
                    }

                    if( bSetCursor )
                    {
                        SwCallLink aLk( *this ); // watch Cursor-Moves
                        SwCursorSaveState aSaveState( *m_pCurrentCursor );
                        m_pCurrentCursor->DeleteMark();
                        *m_pCurrentCursor->GetPoint() = aPos;
                        if( m_pCurrentCursor->IsSelOvr() )
                        {
                            // allow click fields in protected sections
                            // only placeholder is not possible
                            if( IsAttrAtPos::Field & rContentAtPos.eContentAtPos
                                || SwFieldIds::JumpEdit == pField->Which() )
                                pField = nullptr;
                        }
                        else
                            UpdateCursor();
                    }
                    else if( SwFieldIds::Table == pField->Which() &&
                        static_cast<const SwTableField*>(pField)->IsIntrnlName() )
                    {
                        // create from internal (for CORE) the external
                        // (for UI) formula
                        const SwTableNode* pTableNd = pTextNd->FindTableNode();
                        if( pTableNd )        // is in a table
                            const_cast<SwTableField*>(static_cast<const SwTableField*>(pField))->PtrToBoxNm( &pTableNd->GetTable() );
                    }
                }

                if( pField )
                {
                    rContentAtPos.aFnd.pField = pField;
                    rContentAtPos.pFndTextAttr = pTextAttr;
                    rContentAtPos.eContentAtPos = IsAttrAtPos::Field;
                    bRet = true;
                }
            }

            if( !bRet && IsAttrAtPos::FormControl & rContentAtPos.eContentAtPos )
            {
                IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess( );
                sw::mark::Fieldmark* pFieldBookmark = pMarksAccess->getInnerFieldmarkFor(aPos);
                if (bCursorFoundExact && pFieldBookmark)
                {
                    rContentAtPos.eContentAtPos = IsAttrAtPos::FormControl;
                    rContentAtPos.aFnd.pFieldmark = pFieldBookmark;
                    bRet=true;
                }
            }

            if (!bRet && rContentAtPos.eContentAtPos & IsAttrAtPos::ContentControl)
            {
                SwTextAttr* pAttr = pTextNd->GetTextAttrAt(
                    aPos.GetContentIndex(), RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent);
                if (pAttr)
                {
                    rContentAtPos.eContentAtPos = IsAttrAtPos::ContentControl;
                    rContentAtPos.pFndTextAttr = pAttr;
                    bRet = true;
                }
            }

            if( !bRet && IsAttrAtPos::Footnote & rContentAtPos.eContentAtPos )
            {
                if( aTmpState.m_bFootnoteNoInfo )
                {
                    // over the footnote's char
                    bRet = true;
                    if( bSetCursor )
                    {
                        *m_pCurrentCursor->GetPoint() = aPos;
                        if( !GotoFootnoteAnchor() )
                            bRet = false;
                    }
                    if( bRet )
                        rContentAtPos.eContentAtPos = IsAttrAtPos::Footnote;
                }
                else if ( nullptr != ( pTextAttr = pTextNd->GetTextAttrForCharAt(
                    aPos.GetContentIndex(), RES_TXTATR_FTN )) )
                {
                    bRet = true;
                    if( bSetCursor )
                    {
                        if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
                            pWrtSh->addCurrentPosition();

                        SwCallLink aLk( *this ); // watch Cursor-Moves
                        SwCursorSaveState aSaveState( *m_pCurrentCursor );
                        m_pCurrentCursor->GetPoint()->Assign( *static_cast<SwTextFootnote*>(pTextAttr)->GetStartNode() );
                        SwContentNode* pCNd = SwNodes::GoNextSection(
                            m_pCurrentCursor->GetPoint(),
                            true, !IsReadOnlyAvailable() );

                        if( pCNd )
                        {
                            if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
                                SwCursorSelOverFlags::Toggle ))
                                bRet = false;
                            else
                                UpdateCursor();
                        }
                        else
                            bRet = false;
                    }

                    if( bRet )
                    {
                        rContentAtPos.eContentAtPos = IsAttrAtPos::Footnote;
                        rContentAtPos.pFndTextAttr = pTextAttr;
                        rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();

                        if (pFieldRect)
                        {
                            std::pair<Point, bool> tmp(aPt, true);
                            pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                            if (pFrame)
                                pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
                        }
                    }
                }
            }

            if( !bRet
                && ( IsAttrAtPos::ToxMark | IsAttrAtPos::RefMark ) & rContentAtPos.eContentAtPos
                && !aTmpState.m_bFootnoteNoInfo )
            {
                pTextAttr = nullptr;
                if( IsAttrAtPos::ToxMark & rContentAtPos.eContentAtPos )
                {
                    std::vector<SwTextAttr *> const marks(
                        pTextNd->GetTextAttrsAt(
                           aPos.GetContentIndex(), RES_TXTATR_TOXMARK));
                    if (!marks.empty())
                    {   // hmm... can only return 1 here
                        pTextAttr = *marks.begin();
                    }
                }

                if( !pTextAttr &&
                    IsAttrAtPos::RefMark & rContentAtPos.eContentAtPos )
                {
                    std::vector<SwTextAttr *> const marks(
                        pTextNd->GetTextAttrsAt(
                           aPos.GetContentIndex(), RES_TXTATR_REFMARK));
                    if (!marks.empty())
                    {   // hmm... can only return 1 here
                        pTextAttr = *marks.begin();
                    }
                }

                if( pTextAttr )
                {
                    bRet = true;
                    if( bSetCursor )
                    {
                        SwCallLink aLk( *this ); // watch Cursor-Moves
                        SwCursorSaveState aSaveState( *m_pCurrentCursor );
                        m_pCurrentCursor->DeleteMark();
                        *m_pCurrentCursor->GetPoint() = aPos;
                        if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection | SwCursorSelOverFlags::Toggle ) )
                            bRet = false;
                        else
                            UpdateCursor();
                    }

                    if( bRet )
                    {
                        OUStringBuffer aStringBuffer;
                        if (RES_TXTATR_TOXMARK == pTextAttr->Which())
                        {
                            // tdf#143157 - include first and secondary keys in index fields
                            const auto& aSwTOXMark = pTextAttr->GetTOXMark();
                            const auto aSecondaryKey = aSwTOXMark.GetSecondaryKey();

                            aStringBuffer.append(aSwTOXMark.GetPrimaryKey());
                            if (!aSecondaryKey.isEmpty())
                            {
                                if (!aStringBuffer.isEmpty())
                                    aStringBuffer.append(" > ");
                                aStringBuffer.append(aSecondaryKey);
                            }

                            const auto aAlternativeText = aSwTOXMark.GetAlternativeText();
                            if (!aStringBuffer.isEmpty() && !aAlternativeText.isEmpty())
                                aStringBuffer.append(" > ");
                            aStringBuffer.append(aAlternativeText);
                        }

                        // tdf#143157 - include expanded text in index fields
                        const sal_Int32* pEnd = pTextAttr->GetEnd();
                        if (pEnd)
                        {
                            const auto aExpandText = pTextNd->GetExpandText(
                                GetLayout(), pTextAttr->GetStart(), *pEnd - pTextAttr->GetStart());
                            if (!aStringBuffer.isEmpty() && !aExpandText.isEmpty())
                                aStringBuffer.append(" > ");
                            aStringBuffer.append(aExpandText);
                        }
                        rContentAtPos.sStr = aStringBuffer.makeStringAndClear();

                        rContentAtPos.eContentAtPos =
                            RES_TXTATR_TOXMARK == pTextAttr->Which()
                            ? IsAttrAtPos::ToxMark
                            : IsAttrAtPos::RefMark;
                        rContentAtPos.pFndTextAttr = pTextAttr;
                        rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();

                        std::pair<Point, bool> tmp(aPt, true);
                        if (pFieldRect)
                        {
                            pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                            if (pFrame)
                                pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
                        }
                    }
                }
            }

            if ( !bRet
                 && IsAttrAtPos::InetAttr & rContentAtPos.eContentAtPos
                 && !aTmpState.m_bFootnoteNoInfo )
            {
                sal_Int32 index = aPos.GetContentIndex();
                pTextAttr = pTextNd->GetTextAttrAt(index, RES_TXTATR_INETFMT);

                // "detect" only INetAttrs with URLs
                if( pTextAttr && !pTextAttr->GetINetFormat().GetValue().isEmpty() )
                {
                    bRet = true;
                    if( bSetCursor )
                    {
                        SwCursorSaveState aSaveState( *m_pCurrentCursor );
                        SwCallLink aLk( *this ); // watch Cursor-Moves
                        m_pCurrentCursor->DeleteMark();
                        *m_pCurrentCursor->GetPoint() = aPos;
                        if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
                            SwCursorSelOverFlags::Toggle) )
                            bRet = false;
                        else
                            UpdateCursor();
                    }
                    if( bRet )
                    {
                        const sal_Int32 nSt = pTextAttr->GetStart();
                        const sal_Int32 nEnd = *pTextAttr->End();

                        rContentAtPos.sStr = pTextNd->GetExpandText(GetLayout(), nSt, nEnd-nSt);

                        rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();
                        rContentAtPos.eContentAtPos = IsAttrAtPos::InetAttr;
                        rContentAtPos.pFndTextAttr = pTextAttr;

                        if (pFieldRect)
                        {
                            std::pair<Point, bool> tmp(aPt, true);
                            pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                            if (pFrame)
                            {
                                //get bounding box of range
                                SwRect aStart;
                                SwPosition aStartPos(*pTextNd, nSt);
                                pFrame->GetCharRect(aStart, aStartPos, &aTmpState);
                                SwRect aEnd;
                                SwPosition aEndPos(*pTextNd, nEnd);
                                pFrame->GetCharRect(aEnd, aEndPos, &aTmpState);
                                if (aStart.Top() != aEnd.Top() || aStart.Bottom() != aEnd.Bottom())
                                {
                                    aStart.Left(pFrame->getFrameArea().Left());
                                    aEnd.Right(pFrame->getFrameArea().Right());
                                }
                                *pFieldRect = aStart.Union(aEnd);
                            }
                        }
                    }
                }
            }

            if( !bRet && IsAttrAtPos::Redline & rContentAtPos.eContentAtPos )
            {
                SwRedlineTable::size_type index;
                IDocumentRedlineAccess const& rIDRA{GetDoc()->getIDocumentRedlineAccess()};
                const SwRangeRedline* pRedl{rIDRA.GetRedline(aPos, &index)};

                if( pRedl )
                {
                    // treat insert/delete as more important than formatting
                    for (; index < rIDRA.GetRedlineTable().size(); ++index)
                    {
                        SwRangeRedline const*const pTmp{rIDRA.GetRedlineTable()[index]};
                        if (aPos < *pTmp->Start())
                        {
                            break;
                        }
                        switch (pRedl->GetType())
                        {
                            case RedlineType::Format:
                            case RedlineType::FmtColl:
                            case RedlineType::ParagraphFormat:
                                switch (pTmp->GetType())
                                {
                                    case RedlineType::Insert:
                                    case RedlineType::Delete:
                                        pRedl = pTmp;
                                        break;
                                    default:
                                        break;
                                }
                            break;
                            default:
                                break;
                        }
                    };

                    rContentAtPos.aFnd.pRedl = pRedl;
                    rContentAtPos.eContentAtPos = IsAttrAtPos::Redline;
                    rContentAtPos.pFndTextAttr = nullptr;
                    bRet = true;

                    if (pFieldRect)
                    {
                        std::pair<Point, bool> tmp(aPt, true);
                        pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                        if( pFrame )
                        {
                            // not sure if this should be limited to one
                            // paragraph, or mark the entire redline; let's
                            // leave it limited to one for now...
                            sal_Int32 nStart;
                            sal_Int32 nEnd;
                            pRedl->CalcStartEnd(pTextNd->GetIndex(), nStart, nEnd);
                            if (nStart == COMPLETE_STRING)
                            {
                                // consistency: found pRedl, so there must be
                                // something in pTextNd
                                assert(nEnd != COMPLETE_STRING);
                                nStart = 0;
                            }
                            if (nEnd == COMPLETE_STRING)
                            {
                                nEnd = pTextNd->Len();
                            }
                            //get bounding box of range
                            SwRect aStart;
                            pFrame->GetCharRect(aStart, SwPosition(*pTextNd, nStart), &aTmpState);
                            SwRect aEnd;
                            pFrame->GetCharRect(aEnd, SwPosition(*pTextNd, nEnd), &aTmpState);
                            if (aStart.Top() != aEnd.Top() || aStart.Bottom() != aEnd.Bottom())
                            {
                                aStart.Left(pFrame->getFrameArea().Left());
                                aEnd.Right(pFrame->getFrameArea().Right());
                            }
                            *pFieldRect = aStart.Union(aEnd);
                        }
                    }
                }
            }
        }

        if( !bRet && ( ( IsAttrAtPos::TableRedline & rContentAtPos.eContentAtPos ) ||
                     ( IsAttrAtPos::TableColRedline & rContentAtPos.eContentAtPos ) ) )
        {
            const SwTableNode* pTableNd;
            const SwTableBox* pBox;
            const SwTableLine* pTableLine;
            const SwStartNode* pSttNd = pTextNd->FindTableBoxStartNode();
            if( pSttNd && nullptr != ( pTableNd = pTextNd->FindTableNode()) &&
                nullptr != ( pBox = pTableNd->GetTable().GetTableBox(
                pSttNd->GetIndex() )) &&
                nullptr != ( pTableLine = pBox->GetUpper() ) &&
                ( RedlineType::None != pBox->GetRedlineType() ||
                RedlineType::None != pTableLine->GetRedlineType() ) )
            {
                const SwRedlineTable& aRedlineTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
                if ( RedlineType::None != pTableLine->GetRedlineType() )
                {
                    SwRedlineTable::size_type nPos = 0;
                    nPos = pTableLine->UpdateTextChangesOnly(nPos);
                    if ( nPos != SwRedlineTable::npos )
                    {
                        rContentAtPos.aFnd.pRedl = aRedlineTable[nPos];
                        rContentAtPos.eContentAtPos = IsAttrAtPos::TableRedline;
                        bRet = true;
                    }
                }
                else
                {
                    SwRedlineTable::size_type n = 0;
                    SwNodeIndex aIdx( *pSttNd, 1 );
                    const SwPosition aBoxStart(aIdx);
                    const SwRangeRedline* pFnd = aRedlineTable.FindAtPosition( aBoxStart, n, /*next=*/true );
                    if( pFnd && RedlineType::Delete == pFnd->GetType() )
                    {
                        rContentAtPos.aFnd.pRedl = aRedlineTable[n];
                        rContentAtPos.eContentAtPos = IsAttrAtPos::TableColRedline;
                        bRet = true;
                    }
                }
            }
        }

        if( !bRet
             && ( IsAttrAtPos::TableBoxFml & rContentAtPos.eContentAtPos
#ifdef DBG_UTIL
                  || IsAttrAtPos::TableBoxValue & rContentAtPos.eContentAtPos
#endif
            ) )
        {
            const SwTableNode* pTableNd;
            const SwTableBox* pBox;
            const SwStartNode* pSttNd = pTextNd->FindTableBoxStartNode();
            const SwTableBoxFormula* pItem;
#ifdef DBG_UTIL
            const SwTableBoxValue* pItem2 = nullptr;
#endif
            if( pSttNd && nullptr != ( pTableNd = pTextNd->FindTableNode()) &&
                nullptr != ( pBox = pTableNd->GetTable().GetTableBox(
                pSttNd->GetIndex() )) &&
#ifdef DBG_UTIL
                ( (pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMULA, false )) ||
                  (pItem2 = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_VALUE, false )) )
#else
                (pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMULA, false ))
#endif
                )
            {
                std::pair<Point, bool> tmp(aPt, true);
                SwFrame* pF = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                if( pF )
                {
                    // then the CellFrame
                    pFrame = static_cast<SwContentFrame*>(pF);
                    while( pF && !pF->IsCellFrame() )
                        pF = pF->GetUpper();
                }

                if( aTmpState.m_bPosCorr )
                {
                    if( pF && !pF->getFrameArea().Contains( aPt ))
                        pF = nullptr;
                }
                else if( !pF )
                    pF = pFrame;

                if( pF ) // only then it is valid
                {
                    // create from internal (for CORE) the external
                    // (for UI) formula
                    rContentAtPos.eContentAtPos = IsAttrAtPos::TableBoxFml;
#ifdef DBG_UTIL
                    if( pItem2 )
                        rContentAtPos.eContentAtPos = IsAttrAtPos::TableBoxValue;
                    else
#endif
                        const_cast<SwTableBoxFormula&>(*pItem).PtrToBoxNm( &pTableNd->GetTable() );

                    bRet = true;
                    if( bSetCursor )
                    {
                        SwCallLink aLk( *this ); // watch Cursor-Moves
                        SwCursorSaveState aSaveState( *m_pCurrentCursor );
                        *m_pCurrentCursor->GetPoint() = std::move(aPos);
                        if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
                            SwCursorSelOverFlags::Toggle) )
                            bRet = false;
                        else
                            UpdateCursor();
                    }

                    if( bRet )
                    {
                        if( pFieldRect )
                        {
                            *pFieldRect = pF->getFramePrintArea();
                            *pFieldRect += pF->getFrameArea().Pos();
                        }
                        rContentAtPos.pFndTextAttr = nullptr;
                        rContentAtPos.aFnd.pAttr = pItem;
                    }
                }
            }
        }

#ifdef DBG_UTIL
        if( !bRet && IsAttrAtPos::CurrAttrs & rContentAtPos.eContentAtPos )
        {
            const sal_Int32 n = aPos.GetContentIndex();
            SfxItemSetFixed<POOLATTR_BEGIN, POOLATTR_END - 1>  aSet( GetDoc()->GetAttrPool() );
            if( pTextNd->GetpSwpHints() )
            {
                for( size_t i = 0; i < pTextNd->GetSwpHints().Count(); ++i )
                {
                    const SwTextAttr* pHt = pTextNd->GetSwpHints().Get(i);
                    const sal_Int32 nAttrStart = pHt->GetStart();
                    if( nAttrStart > n ) // over the section
                        break;

                    if( nullptr != pHt->End() && (
                        ( nAttrStart < n &&
                        ( pHt->DontExpand() ? n < *pHt->End()
                        : n <= *pHt->End() )) ||
                        ( n == nAttrStart &&
                        ( nAttrStart == *pHt->End() || !n ))) )
                    {
                        aSet.Put( pHt->GetAttr() );
                    }
                }
                if( pTextNd->HasSwAttrSet() &&
                    pTextNd->GetpSwAttrSet()->Count() )
                {
                    SfxItemSet aFormatSet( pTextNd->GetSwAttrSet() );
                    // remove all from format set that are also in TextSet
                    aFormatSet.Differentiate( aSet );
                    // now merge all together
                    aSet.Put( aFormatSet );
                }
            }
            else
                pTextNd->SwContentNode::GetAttr( aSet );

            rContentAtPos.sStr = "Pos: (";
            rContentAtPos.sStr += OUString::number( sal_Int32(aPos.GetNodeIndex()));
            rContentAtPos.sStr += ":";
            rContentAtPos.sStr += OUString::number( aPos.GetContentIndex());
            rContentAtPos.sStr += ")";
            rContentAtPos.sStr += "\nParagraph Style: ";
            rContentAtPos.sStr += pTextNd->GetFormatColl()->GetName().toString();
            if( pTextNd->GetCondFormatColl() )
            {
                rContentAtPos.sStr += "\nConditional Style: " + pTextNd->GetCondFormatColl()->GetName().toString();
            }

            if( aSet.Count() )
            {
                OUStringBuffer sAttrs;
                SfxItemIter aIter( aSet );
                const SfxPoolItem* pItem = aIter.GetCurItem();
                const IntlWrapper aInt(SvtSysLocale().GetUILanguageTag());
                do
                {
                    if( !IsInvalidItem( pItem ))
                    {
                        OUString aStr;
                        GetDoc()->GetAttrPool().GetPresentation(*pItem,
                            MapUnit::MapCM, aStr, aInt);
                        if (!sAttrs.isEmpty())
                            sAttrs.append(", ");
                        sAttrs.append(aStr);
                    }
                    pItem = aIter.NextItem();
                } while (pItem);
                if (!sAttrs.isEmpty())
                {
                    if( !rContentAtPos.sStr.isEmpty() )
                        rContentAtPos.sStr += "\n";
                    rContentAtPos.sStr += "Attr: " + sAttrs;
                }
            }
            bRet = true;
            rContentAtPos.eContentAtPos = IsAttrAtPos::CurrAttrs;
        }
#endif
    }

    if( !bRet )
    {
        rContentAtPos.eContentAtPos = IsAttrAtPos::NONE;
        rContentAtPos.aFnd.pField = nullptr;
    }
    return bRet;
}

// #i90516#
const SwPostItField* SwCursorShell::GetPostItFieldAtCursor() const
{
    if ( IsTableMode() )
        return nullptr;

    const SwPosition* pCursorPos = GetCursor_()->GetPoint();
    const SwTextNode* pTextNd = pCursorPos->GetNode().GetTextNode();
    if ( !pTextNd )
        return nullptr;

    const SwPostItField* pPostItField = nullptr;
    SwTextAttr* pTextAttr = pTextNd->GetFieldTextAttrAt( pCursorPos->GetContentIndex() );
    const SwField* pField = pTextAttr != nullptr ? pTextAttr->GetFormatField().GetField() : nullptr;
    if ( pField && pField->Which()== SwFieldIds::Postit )
    {
        pPostItField = static_cast<const SwPostItField*>(pField);
    }

    return pPostItField;
}

/// is the node in a protected section?
bool SwContentAtPos::IsInProtectSect() const
{
    const SwTextNode* pNd = nullptr;
    if( pFndTextAttr )
    {
        switch( eContentAtPos )
        {
        case IsAttrAtPos::Field:
        case IsAttrAtPos::ClickField:
            pNd = static_txtattr_cast<SwTextField const*>(pFndTextAttr)->GetpTextNode();
            break;

        case IsAttrAtPos::Footnote:
            pNd = &static_cast<const SwTextFootnote*>(pFndTextAttr)->GetTextNode();
            break;

        case IsAttrAtPos::InetAttr:
            pNd = static_txtattr_cast<SwTextINetFormat const*>(pFndTextAttr)->GetpTextNode();
            break;

        default:
            break;
        }
    }

    if( !pNd )
        return false;
    if( pNd->IsInProtectSect() )
        return true;

    const SwContentFrame* pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr);
    return pFrame && pFrame->IsProtected() ;
}

bool SwContentAtPos::IsInRTLText()const
{
    const SwTextNode* pNd = nullptr;
    if (!pFndTextAttr || (eContentAtPos != IsAttrAtPos::Footnote))
        return false;

    const SwTextFootnote* pTextFootnote = static_cast<const SwTextFootnote*>(pFndTextAttr);
    if(!pTextFootnote->GetStartNode())
        return false;

    SwStartNode* pSttNd = pTextFootnote->GetStartNode()->GetNode().GetStartNode();
    SwPaM aTemp( *pSttNd );
    aTemp.Move(fnMoveForward, GoInNode);
    SwContentNode* pContentNode = aTemp.GetPointContentNode();
    if(pContentNode && pContentNode->IsTextNode())
        pNd = pContentNode->GetTextNode();
    if(!pNd)
        return false;

    bool bRet = false;
    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
    SwTextFrame* pTmpFrame = aIter.First();
    while( pTmpFrame )
    {
        if ( !pTmpFrame->IsFollow())
        {
            bRet = pTmpFrame->IsRightToLeft();
            break;
        }
        pTmpFrame = aIter.Next();
    }
    return bRet;
}

bool SwCursorShell::SelectTextModel( const sal_Int32 nStart,
                                 const sal_Int32 nEnd )
{
    CurrShell aCurr( this );
    bool bRet = false;

    SwCallLink aLk( *this );
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    SwPosition& rPos = *m_pCurrentCursor->GetPoint();
    assert(nEnd <= rPos.GetNode().GetTextNode()->Len());
    m_pCurrentCursor->DeleteMark();
    rPos.SetContent(nStart);
    m_pCurrentCursor->SetMark();
    rPos.SetContent(nEnd);

    if( !m_pCurrentCursor->IsSelOvr() )
    {
        UpdateCursor();
        bRet = true;
    }

    return bRet;
}

TextFrameIndex SwCursorShell::GetCursorPointAsViewIndex() const
{
    SwPosition const*const pPos(GetCursor()->GetPoint());
    SwTextNode const*const pTextNode(pPos->GetNode().GetTextNode());
    assert(pTextNode);
    SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTextNode->getLayoutFrame(GetLayout())));
    assert(pFrame);
    return pFrame->MapModelToViewPos(*pPos);
}

bool SwCursorShell::SelectTextView(TextFrameIndex const nStart,
                                 TextFrameIndex const nEnd)
{
    CurrShell aCurr( this );
    bool bRet = false;

    SwCallLink aLk( *this );
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    SwPosition& rPos = *m_pCurrentCursor->GetPoint();
    m_pCurrentCursor->DeleteMark();
    // indexes must correspond to cursor point!
    SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(m_pCurrentCursor->GetPoint()->GetNode().GetTextNode()->getLayoutFrame(GetLayout())));
    assert(pFrame);
    rPos = pFrame->MapViewToModelPos(nStart);
    m_pCurrentCursor->SetMark();
    rPos = pFrame->MapViewToModelPos(nEnd);

    if (!m_pCurrentCursor->IsSelOvr())
    {
        UpdateCursor();
        bRet = true;
    }

    return bRet;
}

bool SwCursorShell::SelectTextAttr( sal_uInt16 nWhich,
                                     bool bExpand,
                                     const SwTextAttr* pTextAttr )
{
    CurrShell aCurr( this );

    if( IsTableMode() )
        return false;

    if( !pTextAttr )
    {
        SwPosition& rPos = *m_pCurrentCursor->GetPoint();
        SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
        pTextAttr = pTextNd
            ? pTextNd->GetTextAttrAt(rPos.GetContentIndex(),
                    nWhich,
                    bExpand ? ::sw::GetTextAttrMode::Expand : ::sw::GetTextAttrMode::Default)
            : nullptr;
    }
    if( !pTextAttr )
        return false;

    const sal_Int32* pEnd = pTextAttr->End();
    sal_Int32 const nEnd(pEnd ? *pEnd : pTextAttr->GetStart() + 1);
    assert(nEnd <= m_pCurrentCursor->GetPoint()->GetNode().GetTextNode()->Len());
    bool bRet = SelectTextModel(pTextAttr->GetStart(), nEnd);
    return bRet;
}

bool SwCursorShell::GotoINetAttr( const SwTextINetFormat& rAttr )
{
    if( !rAttr.GetpTextNode() )
        return false;
    SwCursor* pCursor = getShellCursor( true );

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *pCursor );

    pCursor->GetPoint()->Assign(*rAttr.GetpTextNode(), rAttr.GetStart() );
    bool bRet = !pCursor->IsSelOvr();
    if( bRet )
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return bRet;
}

const SwFormatINetFormat* SwCursorShell::FindINetAttr( std::u16string_view rName ) const
{
    return mxDoc->FindINetAttr( rName );
}

bool SwCursorShell::GetShadowCursorPos( const Point& rPt, SwFillMode eFillMode,
                                SwRect& rRect, sal_Int16& rOrient )
{

    CurrShell aCurr( this );

    if (IsTableMode() || HasSelection()
        || !GetDoc()->GetIDocumentUndoRedo().DoesUndo())
        return false;

    Point aPt( rPt );
    SwPosition aPos( *m_pCurrentCursor->GetPoint() );

    SwFillCursorPos aFPos( eFillMode );
    SwCursorMoveState aTmpState( &aFPos );

    bool bRet = false;
    if( GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState ) &&
        !aPos.GetNode().IsProtect())
    {
        // start position in protected section?
        rRect = aFPos.aCursor;
        rOrient = aFPos.eOrient;
        bRet = true;
    }
    return bRet;
}

bool SwCursorShell::SetShadowCursorPos( const Point& rPt, SwFillMode eFillMode )
{
    CurrShell aCurr( this );

    if (IsTableMode() || HasSelection()
        || !GetDoc()->GetIDocumentUndoRedo().DoesUndo())
        return false;

    Point aPt( rPt );
    SwPosition aPos( *m_pCurrentCursor->GetPoint() );

    SwFillCursorPos aFPos( eFillMode );
    SwCursorMoveState aTmpState( &aFPos );

    if( !GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState ) )
        return false;

    SwCallLink aLk( *this ); // watch Cursor-Moves
    StartAction();

    SwContentNode* pCNd = aPos.GetNode().GetContentNode();
    SwUndoId nUndoId = SwUndoId::INS_FROM_SHADOWCRSR;
    // If only the paragraph attributes "Adjust" or "LRSpace" are set,
    // then the following should not delete those again.
    if( 0 == aFPos.nParaCnt + aFPos.nColumnCnt &&
        ( SwFillMode::Indent == aFPos.eMode ||
          ( text::HoriOrientation::NONE != aFPos.eOrient &&
            0 == aFPos.nTabCnt + aFPos.nSpaceCnt )) &&
        pCNd && pCNd->Len() )
        nUndoId = SwUndoId::EMPTY;

    GetDoc()->GetIDocumentUndoRedo().StartUndo( nUndoId, nullptr );

    SwTextFormatColl* pNextFormat = nullptr;
    SwTextNode* pTNd = pCNd ? pCNd->GetTextNode() : nullptr;
    if( pTNd )
        pNextFormat = &pTNd->GetTextColl()->GetNextTextFormatColl();

    const SwSectionNode* pSectNd = pCNd ? pCNd->FindSectionNode() : nullptr;
    if( pSectNd && aFPos.nParaCnt )
    {
        SwNodeIndex aEnd( aPos.GetNode(), 1 );
        while( aEnd.GetNode().IsEndNode() &&
                &aEnd.GetNode() !=
                pSectNd->EndOfSectionNode() )
            ++aEnd;

        if( aEnd.GetNode().IsEndNode() &&
            pCNd->Len() == aPos.GetContentIndex() )
            aPos.Assign( *pSectNd->EndOfSectionNode() );
    }

    for( sal_uInt16 n = 0; n < aFPos.nParaCnt + aFPos.nColumnCnt; ++n )
    {
        GetDoc()->getIDocumentContentOperations().AppendTextNode( aPos );
        if( !n && pNextFormat )
        {
            *m_pCurrentCursor->GetPoint() = aPos;
            GetDoc()->SetTextFormatColl( *m_pCurrentCursor, pNextFormat, false );
        }
        if( n < aFPos.nColumnCnt )
        {
            *m_pCurrentCursor->GetPoint() = aPos;
            GetDoc()->getIDocumentContentOperations().InsertPoolItem( *m_pCurrentCursor,
                    SvxFormatBreakItem( SvxBreak::ColumnBefore, RES_BREAK ) );
        }
    }

    *m_pCurrentCursor->GetPoint() = aPos;
    switch( aFPos.eMode )
    {
    case SwFillMode::Indent:
        if( nullptr != (pCNd = aPos.GetNode().GetContentNode() ))
        {
            assert(pCNd->IsTextNode()); // ???
            SfxItemSetFixed<
                    RES_PARATR_ADJUST, RES_PARATR_ADJUST,
                    RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT> aSet(GetDoc()->GetAttrPool());
            SvxFirstLineIndentItem firstLine(pCNd->GetAttr(RES_MARGIN_FIRSTLINE));
            SvxTextLeftMarginItem leftMargin(pCNd->GetAttr(RES_MARGIN_TEXTLEFT));
            firstLine.SetTextFirstLineOffset(SvxIndentValue::zero());
            leftMargin.SetTextLeft(SvxIndentValue::twips(aFPos.nTabCnt));
            aSet.Put(firstLine);
            aSet.Put(leftMargin);

            const SvxAdjustItem& rAdj = pCNd->GetAttr(RES_PARATR_ADJUST);
            if( SvxAdjust::Left != rAdj.GetAdjust() )
                aSet.Put( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );

            GetDoc()->getIDocumentContentOperations().InsertItemSet( *m_pCurrentCursor, aSet );
        }
        else {
            OSL_ENSURE( false, "No ContentNode" );
        }
        break;

    case SwFillMode::Tab:
    case SwFillMode::TabSpace:
    case SwFillMode::Space:
        {
            OUString sInsert;
            if (aFPos.eMode == SwFillMode::Space)
            {
                sInsert = OUString::Concat(RepeatedUChar(' ', aFPos.nSpaceOnlyCnt));
            }
            else
            {
                sInsert = RepeatedUChar('\t', aFPos.nTabCnt) + RepeatedUChar(' ', aFPos.nSpaceCnt);
            }
            if (!sInsert.isEmpty())
                GetDoc()->getIDocumentContentOperations().InsertString(*m_pCurrentCursor, sInsert);
        }
        [[fallthrough]]; // still need to set orientation
    case SwFillMode::Margin:
        if( text::HoriOrientation::NONE != aFPos.eOrient )
        {
            SvxAdjustItem aAdj( SvxAdjust::Left, RES_PARATR_ADJUST );
            switch( aFPos.eOrient )
            {
            case text::HoriOrientation::CENTER:
                aAdj.SetAdjust( SvxAdjust::Center );
                break;
            case text::HoriOrientation::RIGHT:
                aAdj.SetAdjust( SvxAdjust::Right );
                break;
            default:
                break;
            }
            GetDoc()->getIDocumentContentOperations().InsertPoolItem( *m_pCurrentCursor, aAdj );
        }
        break;
    }

    GetDoc()->GetIDocumentUndoRedo().EndUndo( nUndoId, nullptr );
    EndAction();

    return true;
}

const SwRangeRedline* SwCursorShell::SelNextRedline()
{
    if( IsTableMode() )
        return nullptr;

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    // ensure point is at the end so alternating SelNext/SelPrev works
    NormalizePam(false);
    const SwRangeRedline* pFnd = GetDoc()->getIDocumentRedlineAccess().SelNextRedline( *m_pCurrentCursor );

    // at the end of the document, go to the start of the document, and try again
    if ( !pFnd )
    {
        GetDoc()->GetDocShell()->GetWrtShell()->StartOfSection();
        pFnd = GetDoc()->getIDocumentRedlineAccess().SelNextRedline( *m_pCurrentCursor );
    }

    if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
        UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    else
        pFnd = nullptr;
    return pFnd;
}

const SwRangeRedline* SwCursorShell::SelPrevRedline()
{
    if( IsTableMode() )
        return nullptr;

    CurrShell aCurr( this );
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    // ensure point is at the start so alternating SelNext/SelPrev works
    NormalizePam(true);
    const SwRangeRedline* pFnd = GetDoc()->getIDocumentRedlineAccess().SelPrevRedline( *m_pCurrentCursor );

    // at the start of the document, go to the end of the document, and try again
    if ( !pFnd )
    {
        GetDoc()->GetDocShell()->GetWrtShell()->EndOfSection();
        pFnd = GetDoc()->getIDocumentRedlineAccess().SelPrevRedline( *m_pCurrentCursor );
    }

    if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
        UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    else
        pFnd = nullptr;
    return pFnd;
}

const SwRangeRedline* SwCursorShell::GotoRedline_( SwRedlineTable::size_type nArrPos, bool bSelect )
{
    const SwRangeRedline* pFnd = nullptr;
    SwCallLink aLk( *this ); // watch Cursor-Moves
    SwCursorSaveState aSaveState( *m_pCurrentCursor );

    pFnd = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable()[ nArrPos ];
    if( !pFnd )
        return nullptr;

    *m_pCurrentCursor->GetPoint() = *pFnd->Start();

    SwPosition* pPtPos = m_pCurrentCursor->GetPoint();
    if( !pPtPos->GetNode().IsContentNode() )
    {
        SwContentNode* pCNd = SwNodes::GoNextSection(pPtPos,
                                true, IsReadOnlyAvailable() );
        if( pCNd )
        {
            if( pPtPos->GetNode() <= pFnd->End()->GetNode() )
                pPtPos->SetContent( 0 );
            else
                pFnd = nullptr;
        }
    }

    if( pFnd && bSelect )
    {
        m_pCurrentCursor->SetMark();
        if( RedlineType::FmtColl == pFnd->GetType() )
        {
            SwContentNode* pCNd = pPtPos->GetNode().GetContentNode();
            m_pCurrentCursor->GetPoint()->SetContent( pCNd->Len() );
            m_pCurrentCursor->GetMark()->Assign( *pCNd, 0 );
        }
        else
            *m_pCurrentCursor->GetPoint() = *pFnd->End();

        pPtPos = m_pCurrentCursor->GetPoint();
        if( !pPtPos->GetNode().IsContentNode() )
        {
            SwContentNode* pCNd = SwNodes::GoPrevSection( pPtPos,
                                        true, IsReadOnlyAvailable() );
            if( pCNd )
            {
                if( pPtPos->GetNode() >= m_pCurrentCursor->GetMark()->GetNode() )
                    pPtPos->SetContent( pCNd->Len() );
                else
                    pFnd = nullptr;
            }
        }
    }

    if( !pFnd )
    {
        m_pCurrentCursor->DeleteMark();
        m_pCurrentCursor->RestoreSavePos();
    }
    else if( bSelect && *m_pCurrentCursor->GetMark() == *m_pCurrentCursor->GetPoint() )
        m_pCurrentCursor->DeleteMark();

    if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
        UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE
                    | SwCursorShell::READONLY );
    else
    {
        pFnd = nullptr;
        if( bSelect )
            m_pCurrentCursor->DeleteMark();
    }
    return pFnd;
}

const SwRangeRedline* SwCursorShell::GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect )
{
    const SwRangeRedline* pFnd = nullptr;
    if( IsTableMode() )
        return nullptr;

    CurrShell aCurr( this );

    const SwRedlineTable& rTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
    const SwRangeRedline* pTmp = rTable[ nArrPos ];
    sal_uInt16 nSeqNo = pTmp->GetSeqNo();
    if( !nSeqNo || !bSelect )
    {
        pFnd = GotoRedline_( nArrPos, bSelect );
        return pFnd;
    }

    bool bCheck = false;
    int nLoopCnt = 2;
    SwRedlineTable::size_type nArrSavPos = nArrPos;

    do {
        pTmp = GotoRedline_( nArrPos, true );

        if( !pFnd )
            pFnd = pTmp;

        if( pTmp && bCheck )
        {
            // Check for overlaps. These can happen when FormatColl-
            // Redlines were stretched over a whole paragraph
            SwPaM* pCur = m_pCurrentCursor;
            SwPaM* pNextPam = pCur->GetNext();
            auto [pCStt, pCEnd] = pCur->StartEnd(); // SwPosition*
            while( pCur != pNextPam )
            {
                auto [pNStt, pNEnd] = pNextPam->StartEnd(); // SwPosition*

                bool bDel = true;
                switch( ::ComparePosition( *pCStt, *pCEnd,
                                           *pNStt, *pNEnd ))
                {
                case SwComparePosition::Inside:         // Pos1 is completely in Pos2
                    if( !pCur->HasMark() )
                    {
                        pCur->SetMark();
                        *pCur->GetMark() = *pNStt;
                    }
                    else
                        *pCStt = *pNStt;
                    *pCEnd = *pNEnd;
                    break;

                case SwComparePosition::Outside:        // Pos2 is completely in Pos1
                case SwComparePosition::Equal:          // Pos1 has same size as Pos2
                    break;

                case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 at beginning
                    if( !pCur->HasMark() )
                        pCur->SetMark();
                    *pCEnd = *pNEnd;
                    break;
                case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at end
                    if( !pCur->HasMark() )
                    {
                        pCur->SetMark();
                        *pCur->GetMark() = *pNStt;
                    }
                    else
                        *pCStt = *pNStt;
                    break;

                default:
                    bDel = false;
                }

                if( bDel )
                {
                    // not needed anymore
                    SwPaM* pPrevPam = pNextPam->GetPrev();
                    delete pNextPam;
                    pNextPam = pPrevPam;
                }
                pNextPam = pNextPam->GetNext();
            }
        }

        SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
                            ? rTable.FindNextOfSeqNo( nArrPos )
                            : rTable.FindPrevOfSeqNo( nArrPos );
        if( SwRedlineTable::npos != nFndPos ||
            ( 0 != ( --nLoopCnt ) && SwRedlineTable::npos != (
                    nFndPos = rTable.FindPrevOfSeqNo( nArrSavPos ))) )
        {
            if( pTmp )
            {
                // create new cursor
                CreateCursor();
                bCheck = true;
            }
            nArrPos = nFndPos;
        }
        else
            nLoopCnt = 0;

    } while( nLoopCnt );
    return pFnd;
}

bool SwCursorShell::SelectNxtPrvHyperlink( bool bNext )
{
    SwNodes& rNds = GetDoc()->GetNodes();
    const SwNode* pBodyEndNd = &rNds.GetEndOfContent();
    const SwNode* pBodySttNd = pBodyEndNd->StartOfSectionNode();
    SwNodeOffset nBodySttNdIdx = pBodySttNd->GetIndex();
    Point aPt;

    SetGetExpField aCmpPos( SwPosition( bNext ? *pBodyEndNd : *pBodySttNd ) );
    SetGetExpField aCurPos( bNext ? *m_pCurrentCursor->End() : *m_pCurrentCursor->Start() );
    if( aCurPos.GetNode() < nBodySttNdIdx )
    {
        const SwContentNode* pCNd = aCurPos.GetNodeFromContent()->GetContentNode();
        std::pair<Point, bool> tmp(aPt, true);
        if (pCNd)
        {
            SwContentFrame* pFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
            if( pFrame )
                aCurPos.SetBodyPos( *pFrame );
        }
    }

    // check first all the hyperlink fields
    {
        const SwTextNode* pTextNd;
        const SwCharFormats* pFormats = GetDoc()->GetCharFormats();
        for( SwCharFormats::size_type n = pFormats->size(); 1 < n; )
        {
            SwIterator<SwTextINetFormat,SwCharFormat> aIter(*(*pFormats)[--n]);

            for( SwTextINetFormat* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
            {
                pTextNd = pFnd->GetpTextNode();
                if( pTextNd && pTextNd->GetNodes().IsDocNodes() )
                {
                    SwTextINetFormat& rAttr = *pFnd;
                    SetGetExpField aPos( *pTextNd, rAttr );
                    if (pTextNd->GetIndex() < nBodySttNdIdx)
                    {
                        std::pair<Point, bool> tmp(aPt, true);
                        SwContentFrame* pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
                        if (pFrame)
                        {
                            aPos.SetBodyPos( *pFrame );
                        }
                    }

                    if( bNext
                        ? ( aPos < aCmpPos && aCurPos < aPos )
                        : ( aCmpPos < aPos && aPos < aCurPos ))
                    {
                        OUString sText(pTextNd->GetExpandText(GetLayout(),
                                        rAttr.GetStart(),
                                        *rAttr.GetEnd() - rAttr.GetStart() ) );

                        sText = sText.replaceAll("\x0a", "");
                        sText = comphelper::string::strip(sText, ' ');

                        if( !sText.isEmpty() )
                            aCmpPos = aPos;
                    }
                }
            }
        }
    }

    // then check all the Flys with a URL or image map
    {
        for(sw::SpzFrameFormat* pSpz: *GetDoc()->GetSpzFrameFormats())
        {
            if (pSpz->Which() != RES_FLYFRMFMT)
                continue;
            auto pFormat = static_cast<SwFlyFrameFormat*>(pSpz);
            const SwFormatURL& rURLItem = pFormat->GetURL();
            if( rURLItem.GetMap() || !rURLItem.GetURL().isEmpty() )
            {
                SwFlyFrame* pFly = pFormat->GetFrame( &aPt );
                SwPosition aTmpPos( *pBodySttNd );
                if( pFly &&
                    GetBodyTextNode( *GetDoc(), aTmpPos, *pFly->GetLower() ) )
                {
                    SetGetExpField aPos( *pFormat, &aTmpPos );

                    if( bNext
                            ? ( aPos < aCmpPos && aCurPos < aPos )
                            : ( aCmpPos < aPos && aPos < aCurPos ))
                        aCmpPos = aPos;
                }
            }
        }
    }

    // found any URL ?
    const SwTextINetFormat* pFndAttr = aCmpPos.GetINetFormat();
    const SwFlyFrameFormat* pFndFormat = aCmpPos.GetFlyFormat();
    if( !pFndAttr && !pFndFormat )
        return false;

    CurrShell aCurr( this );
    SwCallLink aLk( *this );

    bool bRet = false;
    // found a text attribute ?
    if( pFndAttr )
    {
        SwCursorSaveState aSaveState( *m_pCurrentCursor );

        aCmpPos.GetPosOfContent( *m_pCurrentCursor->GetPoint() );
        m_pCurrentCursor->DeleteMark();
        m_pCurrentCursor->SetMark();
        m_pCurrentCursor->GetPoint()->SetContent( *pFndAttr->End() );

        if( !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
        {
            UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|
                                SwCursorShell::READONLY );
            bRet = true;
        }
    }
    // found a draw object ?
    else if( RES_DRAWFRMFMT == pFndFormat->Which() )
    {
        const SdrObject* pSObj = pFndFormat->FindSdrObject();
        if (pSObj)
        {
            static_cast<SwFEShell*>(this)->SelectObj( pSObj->GetCurrentBoundRect().Center() );
            MakeSelVisible();
            bRet = true;
        }
    }
    else // then is it a fly
    {
        SwFlyFrame* pFly = pFndFormat->GetFrame(&aPt);
        if( pFly )
        {
            static_cast<SwFEShell*>(this)->SelectFlyFrame( *pFly );
            MakeSelVisible();
            bRet = true;
        }
    }
    return bRet;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
