/* -*- 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 OUString* 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 OUString* 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 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( 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 OUString& rRefMark, sal_uInt16 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::Ftn & 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::Ftn;
                }
                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::Ftn;
                        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 )
                    {
                        const sal_Int32* pEnd = pTextAttr->GetEnd();
                        if( pEnd )
                            rContentAtPos.sStr =
                                pTextNd->GetExpandText(GetLayout(), pTextAttr->GetStart(), *pEnd - pTextAttr->GetStart());
                        else if( RES_TXTATR_TOXMARK == pTextAttr->Which())
                            rContentAtPos.sStr =
                                pTextAttr->GetTOXMark().GetAlternativeText();
 
                        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 )
            {
                const SwRangeRedline* pRedl = GetDoc()->getIDocumentRedlineAccess().GetRedline(aPos, nullptr);
 
                if( pRedl )
                {
                    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() = 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();
            if( pTextNd->GetCondFormatColl() )
            {
                rContentAtPos.sStr += "\nConditional Style: " + pTextNd->GetCondFormatColl()->GetName();
            }
 
            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::Ftn:
            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::Ftn))
        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(0);
            leftMargin.SetTextLeft(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:
        {
            OUStringBuffer sInsert;
            if (aFPos.eMode == SwFillMode::Space)
            {
                comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceOnlyCnt, ' ');
            }
            else
            {
                if (aFPos.nTabCnt)
                    comphelper::string::padToLength(sInsert, aFPos.nTabCnt, '\t');
                if (aFPos.nSpaceCnt)
                    comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceCnt, ' ');
            }
            if (!sInsert.isEmpty())
                GetDoc()->getIDocumentContentOperations().InsertString( *m_pCurrentCursor, sInsert.makeStringAndClear());
        }
        [[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: */

V530 The return value of function 'padToLength' is required to be utilized.

V530 The return value of function 'padToLength' is required to be utilized.

V530 The return value of function 'padToLength' is required to be utilized.

V522 There might be dereferencing of a potential null pointer 'pTextInputField'.

V522 There might be dereferencing of a potential null pointer 'pTextInputField'.