    /* -*- 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 <com/sun/star/text/XTextRange.hpp>

    #include <hintids.hxx>
    #include <svx/svdmodel.hxx>
    #include <editeng/frmdiritem.hxx>
    #include <sfx2/viewsh.hxx>
    #include <SwSmartTagMgr.hxx>
    #include <doc.hxx>
    #include <rootfrm.hxx>
    #include <pagefrm.hxx>
    #include <cntfrm.hxx>
    #include <viewimp.hxx>
    #include <pam.hxx>
    #include <swselectionlist.hxx>
    #include "BlockCursor.hxx"
    #include <ndtxt.hxx>
    #include <flyfrm.hxx>
    #include <dview.hxx>
    #include <viewopt.hxx>
    #include <frmtool.hxx>
    #include <crsrsh.hxx>
    #include <tabfrm.hxx>
    #include <txtfrm.hxx>
    #include <sectfrm.hxx>
    #include <swtable.hxx>
    #include "callnk.hxx"
    #include <viscrs.hxx>
    #include <section.hxx>
    #include <docsh.hxx>
    #include <scriptinfo.hxx>
    #include <globdoc.hxx>
    #include <pamtyp.hxx>
    #include <mdiexp.hxx>
    #include <fmteiro.hxx>
    #include <wrong.hxx>
    #include <unotextrange.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/settings.hxx>
    #include <numrule.hxx>
    #include <IGrammarContact.hxx>
    #include <comphelper/flagguard.hxx>
    #include <globals.hrc>
    #include <strings.hrc>
    #include <IDocumentLayoutAccess.hxx>
    #include <LibreOfficeKit/LibreOfficeKitEnums.h>
    #include <comphelper/lok.hxx>
    #include <sfx2/lokhelper.hxx>
    #include <editeng/editview.hxx>
    #include <sal/log.hxx>
    #include <PostItMgr.hxx>
    #include <DocumentSettingManager.hxx>
    #include <vcl/uitest/logger.hxx>
    #include <vcl/uitest/eventdescription.hxx>

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

    /**
     * Delete all overlapping Cursors from a Cursor ring.
     * @param pointer to SwCursor (ring)
     */
    void CheckRange( SwCursor* );

    /**
     * Check if pCurrentCursor points into already existing ranges and delete those.
     * @param Pointer to SwCursor object
     */
    void CheckRange( SwCursor* pCurrentCursor )
    {
        const SwPosition *pStt = pCurrentCursor->Start(),
            *pEnd = pCurrentCursor->GetPoint() == pStt ? pCurrentCursor->GetMark() : pCurrentCursor->GetPoint();

        SwPaM *pTmpDel = nullptr,
              *pTmp = pCurrentCursor->GetNext();

        // Search the complete ring
        while( pTmp != pCurrentCursor )
        {
            const SwPosition *pTmpStt = pTmp->Start(),
                            *pTmpEnd = pTmp->GetPoint() == pTmpStt ?
                                            pTmp->GetMark() : pTmp->GetPoint();
            if( *pStt <= *pTmpStt )
            {
                if( *pEnd > *pTmpStt ||
                    ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd ))
                    pTmpDel = pTmp;
            }
            else
                if( *pStt < *pTmpEnd )
                    pTmpDel = pTmp;

             // If Point or Mark is within the Cursor range, we need to remove the old
            // range. Take note that Point does not belong to the range anymore.
            pTmp = pTmp->GetNext();
            delete pTmpDel;         // Remove old range
            pTmpDel = nullptr;
        }
    }

    // SwCursorShell

    SwPaM * SwCursorShell::CreateCursor()
    {
        // don't create new Cursor with active table Selection
        assert(!IsTableMode());

        // New cursor as copy of current one. Add to the ring.
        // Links point to previously created one, ie forward.
        SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor );

        // Hide PaM logically, to avoid undoing the inverting from
        // copied PaM (#i75172#)
        pNew->swapContent(*m_pCurrentCursor);

        m_pCurrentCursor->DeleteMark();

        UpdateCursor( SwCursorShell::SCROLLWIN );
        return pNew;
    }

    /**
     * Delete current Cursor, making the following one the current.
     * Note, this function does not delete anything if there is no other cursor.
     * @return - returns true if there was another cursor and we deleted one.
     */
    void SwCursorShell::DestroyCursor()
    {
        // don't delete Cursor with active table Selection
        assert(!IsTableMode());

        // Is there a next one? Don't do anything if not.
        if(!m_pCurrentCursor->IsMultiSelection())
            return;

        SwCallLink aLk( *this ); // watch Cursor-Moves
        SwCursor* pNextCursor = static_cast<SwCursor*>(m_pCurrentCursor->GetNext());
        delete m_pCurrentCursor;
        m_pCurrentCursor = dynamic_cast<SwShellCursor*>(pNextCursor);
        UpdateCursor();
    }

    /**
     * Create and return a new shell cursor.
     * Simply returns the current shell cursor if there is no selection
     * (HasSelection()).
     */
    SwPaM & SwCursorShell::CreateNewShellCursor()
    {
        if (HasSelection())
        {
            (void) CreateCursor(); // n.b. returns old cursor
        }
        return *GetCursor();
    }

    /**
     * Return the current shell cursor
     * @return - returns current `SwPaM` shell cursor
     */
    SwPaM & SwCursorShell::GetCurrentShellCursor()
    {
        return *GetCursor();
    }

    /**
     * Return pointer to the current shell cursor
     * @return - returns pointer to current `SwPaM` shell cursor
     */
    SwPaM* SwCursorShell::GetCursor( bool bMakeTableCursor ) const
    {
        if( m_pTableCursor )
        {
            if( bMakeTableCursor && m_pTableCursor->IsCursorMovedUpdate() )
            {
                //don't re-create 'parked' cursors
                const SwContentNode* pCNd;
                if( m_pTableCursor->GetPoint()->nNode.GetIndex() &&
                    m_pTableCursor->GetMark()->nNode.GetIndex() &&
                    nullptr != ( pCNd = m_pTableCursor->GetContentNode() ) && pCNd->getLayoutFrame( GetLayout() ) &&
                    nullptr != ( pCNd = m_pTableCursor->GetContentNode(false) ) && pCNd->getLayoutFrame( GetLayout() ) )
                {
                    SwShellTableCursor* pTC = m_pTableCursor;
                    GetLayout()->MakeTableCursors( *pTC );
                }
            }

            if( m_pTableCursor->IsChgd() )
            {
                const_cast<SwCursorShell*>(this)->m_pCurrentCursor =
                    dynamic_cast<SwShellCursor*>(m_pTableCursor->MakeBoxSels( m_pCurrentCursor ));
            }
        }
        return m_pCurrentCursor;
    }

    void SwCursorShell::StartAction()
    {
        if( !ActionPend() )
        {
            // save for update of the ribbon bar
            const SwNode& rNd = m_pCurrentCursor->GetPoint()->nNode.GetNode();
            m_nCurrentNode = rNd.GetIndex();
            m_nCurrentContent = m_pCurrentCursor->GetPoint()->nContent.GetIndex();
            m_nCurrentNdTyp = rNd.GetNodeType();
            if( rNd.IsTextNode() )
                m_nLeftFramePos = SwCallLink::getLayoutFrame( GetLayout(), *rNd.GetTextNode(), m_nCurrentContent, true );
            else
                m_nLeftFramePos = 0;
        }
        SwViewShell::StartAction(); // to the SwViewShell
    }

    void SwCursorShell::EndAction( const bool bIdleEnd, const bool DoSetPosX )
    {
        comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll());
        bool bVis = m_bSVCursorVis;

        sal_uInt16 eFlags = SwCursorShell::CHKRANGE;
        if ( !DoSetPosX )
            eFlags |= SwCursorShell::UPDOWN;

        // Idle-formatting?
        if( bIdleEnd && Imp()->GetRegion() )
        {
            m_pCurrentCursor->Hide();
        }

        // Update all invalid numberings before the last action
        if( 1 == mnStartAction )
            GetDoc()->UpdateNumRule();

        // #i76923#: Don't show the cursor in the SwViewShell::EndAction() - call.
        //           Only the UpdateCursor shows the cursor.
        bool bSavSVCursorVis = m_bSVCursorVis;
        m_bSVCursorVis = false;

        SwViewShell::EndAction( bIdleEnd );   // have SwViewShell go first

        m_bSVCursorVis = bSavSVCursorVis;

        if( ActionPend() )
        {
            if( bVis )    // display SV-Cursor again
                m_pVisibleCursor->Show();

            return;
        }

        if ( !bIdleEnd )
            eFlags |= SwCursorShell::SCROLLWIN;

        UpdateCursor( eFlags, bIdleEnd );      // Show Cursor changes

        {
            SwCallLink aLk( *this );           // Watch cursor moves,
            aLk.nNode = m_nCurrentNode;        // possibly call the link
            aLk.nNdTyp = m_nCurrentNdTyp;
            aLk.nContent = m_nCurrentContent;
            aLk.nLeftFramePos = m_nLeftFramePos;

            if( !m_nCursorMove ||
                ( 1 == m_nCursorMove && m_bInCMvVisportChgd ) )
                // display Cursor & Selections again
                ShowCursors( m_bSVCursorVis );
        }
        // call ChgCall if there is still one
        if( m_bCallChgLnk && m_bChgCallFlag && m_aChgLnk.IsSet() )
        {
            m_aChgLnk.Call( this );
            m_bChgCallFlag = false;       // reset flag
        }
    }

    void SwCursorShell::SttCursorMove()
    {
    #ifdef DBG_UTIL
        OSL_ENSURE( m_nCursorMove < USHRT_MAX, "Too many nested CursorMoves." );
    #endif
        ++m_nCursorMove;
        StartAction();
    }

    void SwCursorShell::EndCursorMove( const bool bIdleEnd )
    {
    #ifdef DBG_UTIL
        OSL_ENSURE( m_nCursorMove, "EndCursorMove() without SttCursorMove()." );
    #endif
        EndAction( bIdleEnd, true );
        --m_nCursorMove;
    #ifdef DBG_UTIL
        if( !m_nCursorMove )
            m_bInCMvVisportChgd = false;
    #endif
    }

    bool SwCursorShell::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode,
                                 bool bVisualAllowed )
    {
        if( IsTableMode() )
            return bLeft ? GoPrevCell() : GoNextCell();

        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        bool bRet = false;

        // #i27615# Handle cursor in front of label.
        const SwTextNode* pTextNd = nullptr;

        if( m_pBlockCursor )
            m_pBlockCursor->clearPoints();

        // 1. CASE: Cursor is in front of label. A move to the right
        // will simply reset the bInFrontOfLabel flag:
        SwShellCursor* pShellCursor = getShellCursor( true );
        if ( !bLeft && pShellCursor->IsInFrontOfLabel() )
        {
            SetInFrontOfLabel( false );
            bRet = true;
        }
        // 2. CASE: Cursor is at beginning of numbered paragraph. A move
        // to the left will simply set the bInFrontOfLabel flag:
        else if ( bLeft && 0 == pShellCursor->GetPoint()->nContent.GetIndex() &&
                 !pShellCursor->IsInFrontOfLabel() && !pShellCursor->HasMark() &&
                 nullptr != ( pTextNd = pShellCursor->GetNode().GetTextNode() ) &&
                 pTextNd->HasVisibleNumberingOrBullet() )
        {
            SetInFrontOfLabel( true );
            bRet = true;
        }
        // 3. CASE: Regular cursor move. Reset the bInFrontOfLabel flag:
        else
        {
            const bool bSkipHidden = !GetViewOptions()->IsShowHiddenChar();
            // #i107447#
            // To avoid loop the reset of <bInFrontOfLabel> flag is no longer
            // reflected in the return value <bRet>.
            const bool bResetOfInFrontOfLabel = SetInFrontOfLabel( false );
            bRet = pShellCursor->LeftRight( bLeft, nCnt, nMode, bVisualAllowed,
                                          bSkipHidden, !IsOverwriteCursor() );
            if ( !bRet && bLeft && bResetOfInFrontOfLabel )
            {
                // undo reset of <bInFrontOfLabel> flag
                SetInFrontOfLabel( true );
            }
        }

        if( bRet )
        {
            UpdateCursor();
        }

        return bRet;
    }

    void SwCursorShell::MarkListLevel( const OUString& sListId,
                                     const int nListLevel )
    {
        if ( sListId != m_sMarkedListId ||
             nListLevel != m_nMarkedListLevel)
        {
            if ( !m_sMarkedListId.isEmpty() )
                mxDoc->MarkListLevel( m_sMarkedListId, m_nMarkedListLevel, false );

            if ( !sListId.isEmpty() )
            {
                mxDoc->MarkListLevel( sListId, nListLevel, true );
            }

            m_sMarkedListId = sListId;
            m_nMarkedListLevel = nListLevel;
        }
    }

    void SwCursorShell::UpdateMarkedListLevel()
    {
        SwTextNode * pTextNd = GetCursor_()->GetNode().GetTextNode();

        if ( pTextNd )
        {
            if ( !pTextNd->IsNumbered() )
            {
                m_pCurrentCursor->SetInFrontOfLabel_( false );
                MarkListLevel( OUString(), 0 );
            }
            else if ( m_pCurrentCursor->IsInFrontOfLabel() )
            {
                if ( pTextNd->IsInList() )
                {
                    assert(pTextNd->GetActualListLevel() >= 0 &&
                           pTextNd->GetActualListLevel() < MAXLEVEL);
                    MarkListLevel( pTextNd->GetListId(),
                                   pTextNd->GetActualListLevel() );
                }
            }
            else
            {
                MarkListLevel( OUString(), 0 );
            }
        }
    }

    void SwCursorShell::FirePageChangeEvent(sal_uInt16 nOldPage, sal_uInt16 nNewPage)
    {
    #ifdef ACCESSIBLE_LAYOUT
        if( Imp()->IsAccessible() )
            Imp()->FirePageChangeEvent( nOldPage, nNewPage );
    #else
        (void)nOldPage;
        (void)nNewPage;
    #endif
    }

    void SwCursorShell::FireColumnChangeEvent(sal_uInt16 nOldColumn, sal_uInt16 nNewColumn)
    {
    #ifdef ACCESSIBLE_LAYOUT
        if( Imp()->IsAccessible() )
            Imp()->FireColumnChangeEvent( nOldColumn,  nNewColumn);
    #else
        (void)nOldColumn;
        (void)nNewColumn;
    #endif
    }

    void SwCursorShell::FireSectionChangeEvent(sal_uInt16 nOldSection, sal_uInt16 nNewSection)
    {
    #ifdef ACCESSIBLE_LAYOUT
        if( Imp()->IsAccessible() )
            Imp()->FireSectionChangeEvent( nOldSection, nNewSection );
    #else
        (void)nOldSection;
        (void)nNewSection;
    #endif
    }

    bool SwCursorShell::bColumnChange()
    {
        SwFrame* pCurrFrame = GetCurrFrame(false);

        if (pCurrFrame == nullptr)
        {
            return false;
        }

        SwFrame* pCurrCol=pCurrFrame->FindColFrame();

        while(pCurrCol== nullptr && pCurrFrame!=nullptr )
        {
            SwLayoutFrame* pParent = pCurrFrame->GetUpper();
            if(pParent!=nullptr)
            {
                pCurrCol=static_cast<SwFrame*>(pParent)->FindColFrame();
                pCurrFrame = static_cast<SwFrame*>(pParent);
            }
            else
            {
                break;
            }
        }

        if(m_oldColFrame == pCurrCol)
            return false;
        else
        {
            m_oldColFrame = pCurrCol;
            return true;
        }
    }

    bool SwCursorShell::UpDown( bool bUp, sal_uInt16 nCnt )
    {
        SET_CURR_SHELL( this );
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed

        bool bTableMode = IsTableMode();
        SwShellCursor* pTmpCursor = getShellCursor( true );

        bool bRet = pTmpCursor->UpDown( bUp, nCnt );
        // #i40019# UpDown should always reset the bInFrontOfLabel flag:
        bRet |= SetInFrontOfLabel(false);

        if( m_pBlockCursor )
            m_pBlockCursor->clearPoints();

        if( bRet )
        {
            m_eMvState = MV_UPDOWN; // status for Cursor travelling - GetCursorOfst
            if( !ActionPend() )
            {
                CursorFlag eUpdateMode = SwCursorShell::SCROLLWIN;
                if( !bTableMode )
                    eUpdateMode = static_cast<CursorFlag>(eUpdateMode
                                | SwCursorShell::UPDOWN | SwCursorShell::CHKRANGE);
                UpdateCursor( static_cast<sal_uInt16>(eUpdateMode) );
            }
        }
        return bRet;
    }

    bool SwCursorShell::LRMargin( bool bLeft, bool bAPI)
    {
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        SET_CURR_SHELL( this );
        m_eMvState = MV_LEFTMARGIN; // status for Cursor travelling - GetCursorOfst

        const bool bTableMode = IsTableMode();
        SwShellCursor* pTmpCursor = getShellCursor( true );

        if( m_pBlockCursor )
            m_pBlockCursor->clearPoints();

        const bool bWasAtLM =
                ( 0 == GetCursor_()->GetPoint()->nContent.GetIndex() );

        bool bRet = pTmpCursor->LeftRightMargin( bLeft, bAPI );

        if ( bLeft && !bTableMode && bRet && bWasAtLM && !GetCursor_()->HasMark() )
        {
            const SwTextNode * pTextNd = GetCursor_()->GetNode().GetTextNode();
            if ( pTextNd && pTextNd->HasVisibleNumberingOrBullet() )
                SetInFrontOfLabel( true );
        }
        else if ( !bLeft )
        {
            bRet = SetInFrontOfLabel( false ) || bRet;
        }

        if( bRet )
        {
            UpdateCursor();
        }
        return bRet;
    }

    bool SwCursorShell::IsAtLRMargin( bool bLeft, bool bAPI ) const
    {
        const SwShellCursor* pTmpCursor = getShellCursor( true );
        return pTmpCursor->IsAtLeftRightMargin( bLeft, bAPI );
    }

    bool SwCursorShell::SttEndDoc( bool bStt )
    {
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed

        SwShellCursor* pTmpCursor = m_pBlockCursor ? &m_pBlockCursor->getShellCursor() : m_pCurrentCursor;
        bool bRet = pTmpCursor->SttEndDoc( bStt );
        if( bRet )
        {
            if( bStt )
                pTmpCursor->GetPtPos().setY( 0 ); // set to 0 explicitly (table header)
            if( m_pBlockCursor )
            {
                m_pBlockCursor->clearPoints();
                RefreshBlockCursor();
            }

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

    void SwCursorShell::ExtendedSelectAll(bool bFootnotes)
    {
        SwNodes& rNodes = GetDoc()->GetNodes();
        SwPosition* pPos = m_pCurrentCursor->GetPoint();
        pPos->nNode = bFootnotes ? rNodes.GetEndOfPostIts() : rNodes.GetEndOfAutotext();
        pPos->nContent.Assign( rNodes.GoNext( &pPos->nNode ), 0 );
        pPos = m_pCurrentCursor->GetMark();
        pPos->nNode = rNodes.GetEndOfContent();
        SwContentNode* pCNd = SwNodes::GoPrevious( &pPos->nNode );
        pPos->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
    }

    bool SwCursorShell::ExtendedSelectedAll()
    {
        SwNodes& rNodes = GetDoc()->GetNodes();
        SwNodeIndex nNode = rNodes.GetEndOfAutotext();
        SwContentNode* pStart = rNodes.GoNext(&nNode);

        nNode = rNodes.GetEndOfContent();
        SwContentNode* pEnd = SwNodes::GoPrevious(&nNode);

        if (!pStart || !pEnd)
            return false;

        SwPosition aStart(*pStart, 0);
        SwPosition aEnd(*pEnd, pEnd->Len());
        SwShellCursor* pShellCursor = getShellCursor(false);
        return aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End();
    }

    bool SwCursorShell::StartsWithTable()
    {
        SwNodes& rNodes = GetDoc()->GetNodes();
        SwNodeIndex nNode(rNodes.GetEndOfExtras());
        SwContentNode* pContentNode = rNodes.GoNext(&nNode);
        return pContentNode->FindTableNode();
    }

    bool SwCursorShell::MovePage( SwWhichPage fnWhichPage, SwPosPage fnPosPage )
    {
        bool bRet = false;

        // never jump of section borders at selection
        if( !m_pCurrentCursor->HasMark() || !m_pCurrentCursor->IsNoContent() )
        {
            SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
            SET_CURR_SHELL( this );

            SwCursorSaveState aSaveState( *m_pCurrentCursor );
            Point& rPt = m_pCurrentCursor->GetPtPos();
            SwContentFrame * pFrame = m_pCurrentCursor->GetContentNode()->
                                getLayoutFrame( GetLayout(), &rPt, m_pCurrentCursor->GetPoint(), false );
            if( pFrame && ( bRet = GetFrameInPage( pFrame, fnWhichPage,
                                               fnPosPage, m_pCurrentCursor )  ) &&
                !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
                                     SwCursorSelOverFlags::ChangePos ))
                UpdateCursor();
            else
                bRet = false;
        }
        return bRet;
    }

    bool SwCursorShell::isInHiddenTextFrame(SwShellCursor* pShellCursor)
    {
        SwContentNode *pCNode = pShellCursor->GetContentNode();
        SwContentFrame  *pFrame = pCNode ?
            pCNode->getLayoutFrame( GetLayout(), &pShellCursor->GetPtPos(), pShellCursor->GetPoint(), false ) : nullptr;
        return !pFrame || (pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow());
    }

    bool SwCursorShell::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & fnPosPara )
    {
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        SwShellCursor* pTmpCursor = getShellCursor( true );
        bool bRet = pTmpCursor->MovePara( fnWhichPara, fnPosPara );
        if( bRet )
        {
            //keep going until we get something visible, i.e. skip
            //over hidden paragraphs, don't get stuck at the start
            //which is what SwCursorShell::UpdateCursorPos will reset
            //the position to if we pass it a position in an
            //invisible hidden paragraph field
            while (isInHiddenTextFrame(pTmpCursor))
            {
                if (!pTmpCursor->MovePara(fnWhichPara, fnPosPara))
                    break;
            }

            UpdateCursor();
        }
        return bRet;
    }

    bool SwCursorShell::MoveSection( SwWhichSection fnWhichSect,
                                    SwMoveFnCollection const & fnPosSect)
    {
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        SwCursor* pTmpCursor = getShellCursor( true );
        bool bRet = pTmpCursor->MoveSection( fnWhichSect, fnPosSect );
        if( bRet )
            UpdateCursor();
        return bRet;

    }

    // position cursor

    static SwFrame* lcl_IsInHeaderFooter( const SwNodeIndex& rIdx, Point& rPt )
    {
        SwFrame* pFrame = nullptr;
        SwContentNode* pCNd = rIdx.GetNode().GetContentNode();
        if( pCNd )
        {
            SwContentFrame *pContentFrame = pCNd->getLayoutFrame( pCNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), &rPt, nullptr, false );
            pFrame = pContentFrame ? pContentFrame->GetUpper() : nullptr;
            while( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame() )
                pFrame = pFrame->IsFlyFrame() ? static_cast<SwFlyFrame*>(pFrame)->AnchorFrame()
                                        : pFrame->GetUpper();
        }
        return pFrame;
    }

    bool SwCursorShell::IsInHeaderFooter( bool* pbInHeader ) const
    {
        Point aPt;
        SwFrame* pFrame = ::lcl_IsInHeaderFooter( m_pCurrentCursor->GetPoint()->nNode, aPt );
        if( pFrame && pbInHeader )
            *pbInHeader = pFrame->IsHeaderFrame();
        return nullptr != pFrame;
    }

    int SwCursorShell::SetCursor( const Point &rLPt, bool bOnlyText, bool bBlock )
    {
        SET_CURR_SHELL( this );

        SwShellCursor* pCursor = getShellCursor( bBlock );
        SwPosition aPos( *pCursor->GetPoint() );
        Point aPt( rLPt );
        Point & rCurrentCursorPt = pCursor->GetPtPos();
        SwCursorMoveState aTmpState( IsTableMode() ? MV_TBLSEL :
                                        bOnlyText ?  MV_SETONLYTEXT : MV_NONE );
        aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();

        SwTextNode * pTextNd = pCursor->GetNode().GetTextNode();

        if ( pTextNd && !IsTableMode() &&
            // #i37515# No bInFrontOfLabel during selection
            !pCursor->HasMark() &&
            pTextNd->HasVisibleNumberingOrBullet() )
        {
            aTmpState.m_bInFrontOfLabel = true; // #i27615#
        }
        else
        {
            aTmpState.m_bInFrontOfLabel = false;
        }

        int bRet = CRSR_POSOLD |
                    ( GetLayout()->GetCursorOfst( &aPos, aPt, &aTmpState )
                        ? 0 : CRSR_POSCHG );

        const bool bOldInFrontOfLabel = IsInFrontOfLabel();
        const bool bNewInFrontOfLabel = aTmpState.m_bInFrontOfLabel;

        pCursor->SetCursorBidiLevel( aTmpState.m_nCursorBidiLevel );

        if( MV_RIGHTMARGIN == aTmpState.m_eState )
            m_eMvState = MV_RIGHTMARGIN;
        // is the new position in header or footer?
        SwFrame* pFrame = lcl_IsInHeaderFooter( aPos.nNode, aPt );
        if( IsTableMode() && !pFrame && aPos.nNode.GetNode().StartOfSectionNode() ==
            pCursor->GetPoint()->nNode.GetNode().StartOfSectionNode() )
            // same table column and not in header/footer -> back
            return bRet;

        if( m_pBlockCursor && bBlock )
        {
            m_pBlockCursor->setEndPoint( rLPt );
            if( !pCursor->HasMark() )
                m_pBlockCursor->setStartPoint( rLPt );
            else if( !m_pBlockCursor->getStartPoint() )
                m_pBlockCursor->setStartPoint( pCursor->GetMkPos() );
        }
        if( !pCursor->HasMark() )
        {
            // is at the same position and if in header/footer -> in the same
            if( aPos == *pCursor->GetPoint() &&
                bOldInFrontOfLabel == bNewInFrontOfLabel )
            {
                if( pFrame )
                {
                    if( pFrame->getFrameArea().IsInside( rCurrentCursorPt ))
                        return bRet;
                }
                else if( aPos.nNode.GetNode().IsContentNode() )
                {
                    // in the same frame?
                    SwFrame* pOld = static_cast<SwContentNode&>(aPos.nNode.GetNode()).getLayoutFrame(
                                    GetLayout(), &m_aCharRect.Pos(), nullptr, false );
                    SwFrame* pNew = static_cast<SwContentNode&>(aPos.nNode.GetNode()).getLayoutFrame(
                                    GetLayout(), &aPt, nullptr, false );
                    if( pNew == pOld )
                        return bRet;
                }
            }
        }
        else
        {
            // SSelection over not allowed sections or if in header/footer -> different
            if( !CheckNodesRange( aPos.nNode, pCursor->GetMark()->nNode, true )
                || ( pFrame && !pFrame->getFrameArea().IsInside( pCursor->GetMkPos() ) ))
                return bRet;

            // is at same position but not in header/footer
            if( aPos == *pCursor->GetPoint() )
                return bRet;
        }

        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        SwCursorSaveState aSaveState( *pCursor );

        *pCursor->GetPoint() = aPos;
        rCurrentCursorPt = aPt;

        // #i41424# Only update the marked number levels if necessary
        // Force update of marked number levels if necessary.
        if ( bNewInFrontOfLabel || bOldInFrontOfLabel )
            m_pCurrentCursor->SetInFrontOfLabel_( !bNewInFrontOfLabel );
        SetInFrontOfLabel( bNewInFrontOfLabel );

        if( !pCursor->IsSelOvr( SwCursorSelOverFlags::ChangePos ) )
        {
            UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE );
            bRet &= ~CRSR_POSOLD;
        }
        else if( bOnlyText && !m_pCurrentCursor->HasMark() )
        {
            if( FindValidContentNode( bOnlyText ) )
            {
                // position cursor in a valid content
                if( aPos == *pCursor->GetPoint() )
                    bRet = CRSR_POSOLD;
                else
                {
                    UpdateCursor();
                    bRet &= ~CRSR_POSOLD;
                }
            }
            else
            {
                // there is no valid content -> hide cursor
                m_pVisibleCursor->Hide(); // always hide visible cursor
                m_eMvState = MV_NONE; // status for Cursor travelling
                m_bAllProtect = true;
                if( GetDoc()->GetDocShell() )
                {
                    GetDoc()->GetDocShell()->SetReadOnlyUI();
                    CallChgLnk(); // notify UI
                }
            }
        }
        return bRet;
    }

    void SwCursorShell::TableCursorToCursor()
    {
        assert(m_pTableCursor);
        delete m_pTableCursor;
        m_pTableCursor = nullptr;
    }

    void SwCursorShell::BlockCursorToCursor()
    {
        assert(m_pBlockCursor);
        if( m_pBlockCursor && !HasSelection() )
        {
            SwPaM& rPam = m_pBlockCursor->getShellCursor();
            m_pCurrentCursor->SetMark();
            *m_pCurrentCursor->GetPoint() = *rPam.GetPoint();
            if( rPam.HasMark() )
                *m_pCurrentCursor->GetMark() = *rPam.GetMark();
            else
                m_pCurrentCursor->DeleteMark();
        }
        delete m_pBlockCursor;
        m_pBlockCursor = nullptr;
    }

    void SwCursorShell::CursorToBlockCursor()
    {
        if( !m_pBlockCursor )
        {
            SwPosition aPos( *m_pCurrentCursor->GetPoint() );
            m_pBlockCursor = new SwBlockCursor( *this, aPos );
            SwShellCursor &rBlock = m_pBlockCursor->getShellCursor();
            rBlock.GetPtPos() = m_pCurrentCursor->GetPtPos();
            if( m_pCurrentCursor->HasMark() )
            {
                rBlock.SetMark();
                *rBlock.GetMark() = *m_pCurrentCursor->GetMark();
                rBlock.GetMkPos() = m_pCurrentCursor->GetMkPos();
            }
        }
        m_pBlockCursor->clearPoints();
        RefreshBlockCursor();
    }

    void SwCursorShell::ClearMark()
    {
        // is there any GetMark?
        if( m_pTableCursor )
        {
            std::vector<SwPaM*> vCursors;
            for(auto& rCursor : m_pCurrentCursor->GetRingContainer())
                if(&rCursor != m_pCurrentCursor)
                    vCursors.push_back(&rCursor);
            for(auto pCursor : vCursors)
                delete pCursor;
            m_pTableCursor->DeleteMark();

            m_pCurrentCursor->DeleteMark();

            *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();
            m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos();
            delete m_pTableCursor;
            m_pTableCursor = nullptr;
            m_pCurrentCursor->SwSelPaintRects::Show();
        }
        else
        {
            if( !m_pCurrentCursor->HasMark() )
                return;
            m_pCurrentCursor->DeleteMark();
            if( !m_nCursorMove )
                m_pCurrentCursor->SwSelPaintRects::Show();
        }
    }

    void SwCursorShell::NormalizePam(bool bPointFirst)
    {
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        m_pCurrentCursor->Normalize(bPointFirst);
    }

    void SwCursorShell::SwapPam()
    {
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        m_pCurrentCursor->Exchange();
    }

    //TODO: provide documentation
    /** Search in the selected area for a Selection that covers the given point.

        It checks if a Selection exists but does
        not move the current cursor.

        @param rPt      The point to search at.
        @param bTstHit ???
    */
    bool SwCursorShell::TestCurrPam(
        const Point & rPt,
        bool bTstHit )
    {
        SET_CURR_SHELL( this );

        // check if the SPoint is in a table selection
        if( m_pTableCursor )
            return m_pTableCursor->IsInside( rPt );

        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        // search position <rPt> in document
        SwPosition aPtPos( *m_pCurrentCursor->GetPoint() );
        Point aPt( rPt );

        SwCursorMoveState aTmpState( MV_NONE );
        aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
        if ( !GetLayout()->GetCursorOfst( &aPtPos, aPt, &aTmpState ) && bTstHit )
            return false;

        // search in all selections for this position
        SwShellCursor* pCmp = m_pCurrentCursor; // keep the pointer on cursor
        do
        {
            if (pCmp->HasMark() && *pCmp->Start() <= aPtPos && *pCmp->End() > aPtPos)
                return true;               // return without update
            pCmp = pCmp->GetNext();
        } while (m_pCurrentCursor != pCmp);
        return false;
    }

    void SwCursorShell::KillPams()
    {
        // Does any exist for deletion?
        if( !m_pTableCursor && !m_pBlockCursor && !m_pCurrentCursor->IsMultiSelection() )
            return;

        while( m_pCurrentCursor->GetNext() != m_pCurrentCursor )
            delete m_pCurrentCursor->GetNext();
        m_pCurrentCursor->SetColumnSelection( false );

        if( m_pTableCursor )
        {
            // delete the ring of cursors
            m_pCurrentCursor->DeleteMark();
            *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();
            m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos();
            delete m_pTableCursor;
            m_pTableCursor = nullptr;
        }
        else if( m_pBlockCursor )
        {
            // delete the ring of cursors
            m_pCurrentCursor->DeleteMark();
            SwShellCursor &rBlock = m_pBlockCursor->getShellCursor();
            *m_pCurrentCursor->GetPoint() = *rBlock.GetPoint();
            m_pCurrentCursor->GetPtPos() = rBlock.GetPtPos();
            rBlock.DeleteMark();
            m_pBlockCursor->clearPoints();
        }
        UpdateCursor( SwCursorShell::SCROLLWIN );
    }

    int SwCursorShell::CompareCursorStackMkCurrPt() const
    {
        int nRet = 0;
        const SwPosition *pFirst = nullptr, *pSecond = nullptr;
        const SwPaM *pCur = GetCursor(), *pStack = m_pStackCursor;
        // cursor on stack is needed if we compare against stack
        if( pStack  )
        {
            pFirst = pStack->GetMark();
            pSecond = pCur->GetPoint();
        }
        if( !pFirst || !pSecond )
            nRet = INT_MAX;
        else if( *pFirst < *pSecond )
            nRet = -1;
        else if( *pFirst == *pSecond )
            nRet = 0;
        else
            nRet = 1;
        return nRet;
    }

    bool SwCursorShell::IsSttPara() const
    {   return m_pCurrentCursor->GetPoint()->nContent == 0; }

    bool SwCursorShell::IsEndPara() const
    {   return m_pCurrentCursor->GetPoint()->nContent == m_pCurrentCursor->GetContentNode()->Len(); }

    bool SwCursorShell::IsEndOfTable() const
    {
        if (IsTableMode() || IsBlockMode() || !IsEndPara())
        {
            return false;
        }
        SwTableNode const*const pTableNode( IsCursorInTable() );
        if (!pTableNode)
        {
            return false;
        }
        SwEndNode const*const pEndTableNode(pTableNode->EndOfSectionNode());
        SwNodeIndex const lastNode(*pEndTableNode, -2);
        SAL_WARN_IF(!lastNode.GetNode().GetTextNode(), "sw.core",
                "text node expected");
        return (lastNode == m_pCurrentCursor->GetPoint()->nNode);
    }

    bool SwCursorShell::IsCursorInFootnote() const
    {
        SwStartNodeType aStartNodeType = m_pCurrentCursor->GetNode().StartOfSectionNode()->GetStartNodeType();
        return aStartNodeType == SwStartNodeType::SwFootnoteStartNode;
    }

    bool SwCursorShell::IsInFrontOfLabel() const
    {
        return m_pCurrentCursor->IsInFrontOfLabel();
    }

    bool SwCursorShell::SetInFrontOfLabel( bool bNew )
    {
        if ( bNew != IsInFrontOfLabel() )
        {
            m_pCurrentCursor->SetInFrontOfLabel_( bNew );
            UpdateMarkedListLevel();
            return true;
        }
        return false;
    }

    namespace {

    void collectUIInformation(const OUString& aPage)
    {
        EventDescription aDescription;
        aDescription.aAction = "GOTO";
        aDescription.aParameters = {{"PAGE", aPage}};
        aDescription.aID = "writer_edit";
        aDescription.aKeyWord = "SwEditWinUIObject";
        aDescription.aParent = "MainWindow";
        UITestLogger::getInstance().logEvent(aDescription);
    }

    }

    bool SwCursorShell::GotoPage( sal_uInt16 nPage )
    {
        SET_CURR_SHELL( this );
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        SwCursorSaveState aSaveState( *m_pCurrentCursor );
        bool bRet = GetLayout()->SetCurrPage( m_pCurrentCursor, nPage ) &&
                        !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
                                             SwCursorSelOverFlags::ChangePos );
        if( bRet )
            UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);

        collectUIInformation(OUString::number(nPage));
        return bRet;
    }

    void SwCursorShell::GetCharRectAt(SwRect& rRect, const SwPosition* pPos)
    {
        SwContentFrame* pFrame = GetCurrFrame();
        pFrame->GetCharRect( rRect, *pPos );
    }

    void SwCursorShell::GetPageNum( sal_uInt16 &rnPhyNum, sal_uInt16 &rnVirtNum,
                                  bool bAtCursorPos, const bool bCalcFrame )
    {
        SET_CURR_SHELL( this );
        // page number: first visible page or the one at the cursor
        const SwContentFrame* pCFrame;
        const SwPageFrame *pPg = nullptr;

        if( !bAtCursorPos || nullptr == (pCFrame = GetCurrFrame( bCalcFrame )) ||
                           nullptr == (pPg   = pCFrame->FindPageFrame()) )
        {
            pPg = Imp()->GetFirstVisPage(GetOut());
            while( pPg && pPg->IsEmptyPage() )
                pPg = static_cast<const SwPageFrame *>(pPg->GetNext());
        }
        // pPg has to exist with a default of 1 for the special case "Writerstart"
        rnPhyNum  = pPg? pPg->GetPhyPageNum() : 1;
        rnVirtNum = pPg? pPg->GetVirtPageNum() : 1;
    }

    sal_uInt16 SwCursorShell::GetPageNumSeqNonEmpty()
    {
        SET_CURR_SHELL(this);
        // page number: first visible page or the one at the cursor
        const SwContentFrame* pCFrame = GetCurrFrame(/*bCalcFrame*/true);
        const SwPageFrame* pPg = nullptr;

        if (!pCFrame || nullptr == (pPg = pCFrame->FindPageFrame()))
        {
            pPg = Imp()->GetFirstVisPage(GetOut());
            while (pPg && pPg->IsEmptyPage())
                pPg = static_cast<const SwPageFrame*>(pPg->GetNext());
        }

        sal_uInt16 nPageNo = 0;
        while (pPg)
        {
            if (!pPg->IsEmptyPage())
                ++nPageNo;
            pPg = static_cast<const SwPageFrame*>(pPg->GetPrev());
        }
        return nPageNo;
    }

    sal_uInt16 SwCursorShell::GetNextPrevPageNum( bool bNext )
    {
        SET_CURR_SHELL( this );
        // page number: first visible page or the one at the cursor
        const SwPageFrame *pPg = Imp()->GetFirstVisPage(GetOut());
        if( pPg )
        {
            const SwTwips nPageTop = pPg->getFrameArea().Top();

            if( bNext )
            {
                // go to next view layout row:
                do
                {
                    pPg = static_cast<const SwPageFrame *>(pPg->GetNext());
                }
                while( pPg && pPg->getFrameArea().Top() == nPageTop );

                while( pPg && pPg->IsEmptyPage() )
                    pPg = static_cast<const SwPageFrame *>(pPg->GetNext());
            }
            else
            {
                // go to previous view layout row:
                do
                {
                    pPg = static_cast<const SwPageFrame *>(pPg->GetPrev());
                }
                while( pPg && pPg->getFrameArea().Top() == nPageTop );

                while( pPg && pPg->IsEmptyPage() )
                    pPg = static_cast<const SwPageFrame *>(pPg->GetPrev());
            }
        }
        // pPg has to exist with a default of 1 for the special case "Writerstart"
        return pPg ? pPg->GetPhyPageNum() : USHRT_MAX;
    }

    sal_uInt16 SwCursorShell::GetPageCnt()
    {
        SET_CURR_SHELL( this );
        // return number of pages
        return GetLayout()->GetPageNum();
    }

    OUString SwCursorShell::getPageRectangles()
    {
        CurrShell aCurr(this);
        SwRootFrame* pLayout = GetLayout();
        OUStringBuffer aBuf;
        for (const SwFrame* pFrame = pLayout->GetLower(); pFrame; pFrame = pFrame->GetNext())
        {
            aBuf.append(pFrame->getFrameArea().Left());
            aBuf.append(", ");
            aBuf.append(pFrame->getFrameArea().Top());
            aBuf.append(", ");
            aBuf.append(pFrame->getFrameArea().Width());
            aBuf.append(", ");
            aBuf.append(pFrame->getFrameArea().Height());
            aBuf.append("; ");
        }
        if (!aBuf.isEmpty())
            aBuf.setLength( aBuf.getLength() - 2); // remove the last "; "
        return aBuf.makeStringAndClear();
    }

    void SwCursorShell::NotifyCursor(SfxViewShell* pOtherShell) const
    {
        auto pView = const_cast<SdrView*>(GetDrawView());
        if (pView->GetTextEditObject())
        {
            // Blinking cursor.
            EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
            rEditView.RegisterOtherShell(pOtherShell);
            rEditView.ShowCursor();
            rEditView.RegisterOtherShell(nullptr);
            // Text selection, if any.
            rEditView.DrawSelectionXOR(pOtherShell);

            // Shape text lock.
            if (OutlinerView* pOutlinerView = pView->GetTextEditOutlinerView())
            {
                OString sRect = pOutlinerView->GetOutputArea().toString();
                SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRect);
            }
        }
        else
        {
            // Cursor position.
            m_pVisibleCursor->SetPosAndShow(pOtherShell);
            // Cursor visibility.
            if (GetSfxViewShell() != pOtherShell)
            {
                OString aPayload = OString::boolean(m_bSVCursorVis);
                SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload);
            }
            // Text selection.
            m_pCurrentCursor->Show(pOtherShell);
            // Graphic selection.
            pView->AdjustMarkHdl(pOtherShell);
        }
    }

    /// go to the next SSelection
    bool SwCursorShell::GoNextCursor()
    {
        if( !m_pCurrentCursor->IsMultiSelection() )
            return false;

        SET_CURR_SHELL( this );
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        m_pCurrentCursor = m_pCurrentCursor->GetNext();

        // #i24086#: show also all others
        if( !ActionPend() )
        {
            UpdateCursor();
            m_pCurrentCursor->Show(nullptr);
        }
        return true;
    }

    /// go to the previous SSelection
    bool SwCursorShell::GoPrevCursor()
    {
        if( !m_pCurrentCursor->IsMultiSelection() )
            return false;

        SET_CURR_SHELL( this );
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        m_pCurrentCursor = m_pCurrentCursor->GetPrev();

        // #i24086#: show also all others
        if( !ActionPend() )
        {
            UpdateCursor();
            m_pCurrentCursor->Show(nullptr);
        }
        return true;
    }

    void SwCursorShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect)
    {
        comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll());
        SET_CURR_SHELL( this );

        // always switch off all cursors when painting
        SwRect aRect( rRect );

        bool bVis = false;
        // if a cursor is visible then hide the SV cursor
        if( m_pVisibleCursor->IsVisible() && !aRect.IsOver( m_aCharRect ) )
        {
            bVis = true;
            m_pVisibleCursor->Hide();
        }

        // re-paint area
        SwViewShell::Paint(rRenderContext, rRect);

        if( m_bHasFocus && !m_bBasicHideCursor )
        {
            SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;

            if( !ActionPend() )
            {
                // so that right/bottom borders will not be cropped
                pCurrentCursor->Invalidate( VisArea() );
                pCurrentCursor->Show(nullptr);
            }
            else
                pCurrentCursor->Invalidate( aRect );

        }

        if (SwPostItMgr* pPostItMgr = GetPostItMgr())
        {
            // No point in showing the cursor for Writer text when there is an
            // active annotation edit.
            if (bVis)
                bVis = !pPostItMgr->HasActiveSidebarWin();
        }

        if( m_bSVCursorVis && bVis ) // also show SV cursor again
            m_pVisibleCursor->Show();
    }

    void SwCursorShell::VisPortChgd( const SwRect & rRect )
    {
        SET_CURR_SHELL( this );
        bool bVis; // switch off all cursors when scrolling

        // if a cursor is visible then hide the SV cursor
        bVis = m_pVisibleCursor->IsVisible();
        if( bVis )
            m_pVisibleCursor->Hide();

        m_bVisPortChgd = true;
        m_aOldRBPos.setX(VisArea().Right());
        m_aOldRBPos.setY(VisArea().Bottom());

        // For not having problems with the SV cursor, Update() is called for the
        // Window in SwViewShell::VisPo...
        // During painting no selections should be shown, thus the call is encapsulated. <- TODO: old artefact?
        SwViewShell::VisPortChgd( rRect ); // move area

        if( m_bSVCursorVis && bVis ) // show SV cursor again
            m_pVisibleCursor->Show();

        if( m_nCursorMove )
            m_bInCMvVisportChgd = true;

        m_bVisPortChgd = false;
    }

    /** Set the cursor back into content.

        This should only be called if the cursor was move somewhere else (e.g. when
        deleting a border). The new position is calculated from its current position
        in the layout.
    */
    void SwCursorShell::UpdateCursorPos()
    {
        SET_CURR_SHELL( this );
        ++mnStartAction;
        SwShellCursor* pShellCursor = getShellCursor( true );
        Size aOldSz( GetDocSize() );

        if( isInHiddenTextFrame(pShellCursor) )
        {
            SwCursorMoveState aTmpState( MV_NONE );
            aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
            GetLayout()->GetCursorOfst( pShellCursor->GetPoint(), pShellCursor->GetPtPos(),
                                         &aTmpState );
            pShellCursor->DeleteMark();
        }
        IGrammarContact *pGrammarContact = GetDoc() ? GetDoc()->getGrammarContact() : nullptr;
        if( pGrammarContact )
            pGrammarContact->updateCursorPosition( *m_pCurrentCursor->GetPoint() );
        --mnStartAction;
        if( aOldSz != GetDocSize() )
            SizeChgNotify();
    }

    // #i65475# - if Point/Mark in hidden sections, move them out
    static bool lcl_CheckHiddenSection( SwNodeIndex& rIdx )
    {
        bool bOk = true;
        const SwSectionNode* pSectNd = rIdx.GetNode().FindSectionNode();
        if( pSectNd && pSectNd->GetSection().IsHiddenFlag() )
        {
            SwNodeIndex aTmp( *pSectNd );
            const SwNode* pFrameNd =
                rIdx.GetNodes().FindPrvNxtFrameNode( aTmp, pSectNd->EndOfSectionNode() );
            bOk = pFrameNd != nullptr;
            SAL_WARN_IF(!bOk, "sw.core", "found no Node with Frames");
            rIdx = aTmp;
        }
        return bOk;
    }

    /// Try to set the cursor to the next visible content node.
    static void lcl_CheckHiddenPara( SwPosition& rPos )
    {
        SwNodeIndex aTmp( rPos.nNode );
        SwTextNode* pTextNd = aTmp.GetNode().GetTextNode();
        while( pTextNd && pTextNd->HasHiddenCharAttribute( true ) )
        {
            SwContentNode* pContent = aTmp.GetNodes().GoNext( &aTmp );
            if ( pContent && pContent->IsTextNode() )
                pTextNd = pContent->GetTextNode();
            else
                pTextNd = nullptr;
        }

        if ( pTextNd )
            rPos = SwPosition( aTmp, SwIndex( pTextNd, 0 ) );
    }

    // #i27301# - helper class that notifies the accessibility about invalid text
    // selections in its destructor
    class SwNotifyAccAboutInvalidTextSelections
    {
        private:
            SwCursorShell& mrCursorSh;

        public:
            explicit SwNotifyAccAboutInvalidTextSelections( SwCursorShell& _rCursorSh )
                : mrCursorSh( _rCursorSh )
            {}

            ~SwNotifyAccAboutInvalidTextSelections() COVERITY_NOEXCEPT_FALSE
            {
                mrCursorSh.InvalidateAccessibleParaTextSelection();
            }
    };

    void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd )
    {
        SET_CURR_SHELL( this );
        ClearUpCursors();

        if (ActionPend())
        {
            if ( eFlags & SwCursorShell::READONLY )
                m_bIgnoreReadonly = true;
            return; // if not then no update
        }

        SwNotifyAccAboutInvalidTextSelections aInvalidateTextSelections( *this );

        if ( m_bIgnoreReadonly )
        {
            m_bIgnoreReadonly = false;
            eFlags |= SwCursorShell::READONLY;
        }

        if( eFlags & SwCursorShell::CHKRANGE )    // check all cursor moves for
            CheckRange( m_pCurrentCursor );             // overlapping ranges

        if( !bIdleEnd )
            CheckTableBoxContent();

        // If the current cursor is in a table and point/mark in different boxes,
        // then the table mode is active (also if it is already active: m_pTableCursor)
        SwPaM* pTstCursor = getShellCursor( true );
        if( pTstCursor->HasMark() && !m_pBlockCursor &&
            mxDoc->IsIdxInTable( pTstCursor->GetPoint()->nNode ) &&
              ( m_pTableCursor ||
                pTstCursor->GetNode().StartOfSectionNode() !=
                pTstCursor->GetNode( false ).StartOfSectionNode() ) && !mbSelectAll)
        {
            SwShellCursor* pITmpCursor = getShellCursor( true );
            Point aTmpPt( pITmpCursor->GetPtPos() );
            Point aTmpMk( pITmpCursor->GetMkPos() );
            SwPosition* pPos = pITmpCursor->GetPoint();

            // Bug 65475 (1999) - if Point/Mark in hidden sections, move them out
            lcl_CheckHiddenSection( pPos->nNode );
            lcl_CheckHiddenSection( pITmpCursor->GetMark()->nNode );

            // Move cursor out of hidden paragraphs
            if ( !GetViewOptions()->IsShowHiddenChar() )
            {
                lcl_CheckHiddenPara( *pPos );
                lcl_CheckHiddenPara( *pITmpCursor->GetMark() );
            }

            SwContentFrame *pTableFrame = pPos->nNode.GetNode().GetContentNode()->
                                  getLayoutFrame( GetLayout(), &aTmpPt, pPos, false );

            OSL_ENSURE( pTableFrame, "Table Cursor not in Content ??" );

            // --> Make code robust. The table cursor may point
            // to a table in a currently inactive header.
            SwTabFrame *pTab = pTableFrame ? pTableFrame->FindTabFrame() : nullptr;

            if ( pTab && pTab->GetTable()->GetRowsToRepeat() > 0 )
            {
                // First check if point is in repeated headline:
                bool bInRepeatedHeadline = pTab->IsFollow() && pTab->IsInHeadline( *pTableFrame );

                // Second check if mark is in repeated headline:
                if ( !bInRepeatedHeadline )
                {
                    SwContentFrame* pMarkTableFrame = pITmpCursor->GetContentNode( false )->
                        getLayoutFrame( GetLayout(), &aTmpMk, pITmpCursor->GetMark(), false );
                    OSL_ENSURE( pMarkTableFrame, "Table Cursor not in Content ??" );

                    if ( pMarkTableFrame )
                    {
                        SwTabFrame* pMarkTab = pMarkTableFrame->FindTabFrame();
                        OSL_ENSURE( pMarkTab, "Table Cursor not in Content ??" );

                        // Make code robust:
                        if ( pMarkTab )
                        {
                            bInRepeatedHeadline = pMarkTab->IsFollow() && pMarkTab->IsInHeadline( *pMarkTableFrame );
                        }
                    }
                }

                // No table cursor in repeated headlines:
                if ( bInRepeatedHeadline )
                {
                    pTableFrame = nullptr;

                    SwMoveFnCollection const & fnPosSect = *pPos <  *pITmpCursor->GetMark()
                                                ? fnSectionStart
                                                : fnSectionEnd;

                    // then only select inside the Box
                    if( m_pTableCursor )
                    {
                        m_pCurrentCursor->SetMark();
                        *m_pCurrentCursor->GetMark() = *m_pTableCursor->GetMark();
                        m_pCurrentCursor->GetMkPos() = m_pTableCursor->GetMkPos();
                        m_pTableCursor->DeleteMark();
                        m_pTableCursor->SwSelPaintRects::Hide();
                    }

                    *m_pCurrentCursor->GetPoint() = *m_pCurrentCursor->GetMark();
                    GoCurrSection( *m_pCurrentCursor, fnPosSect );
                }
            }

            // we really want a table selection
            if( pTab && pTableFrame )
            {
                if( !m_pTableCursor )
                {
                    m_pTableCursor = new SwShellTableCursor( *this,
                                    *m_pCurrentCursor->GetMark(), m_pCurrentCursor->GetMkPos(),
                                    *pPos, aTmpPt );
                    m_pCurrentCursor->DeleteMark();
                    m_pCurrentCursor->SwSelPaintRects::Hide();

                    CheckTableBoxContent();
                    if(!m_pTableCursor)
                    {
                        SAL_WARN("sw.core", "fdo#74854: "
                            "this should not happen, but better lose the selection "
                            "rather than crashing");
                        return;
                    }
                }

                SwCursorMoveState aTmpState( MV_NONE );
                aTmpState.m_bRealHeight = true;
                {
                    DisableCallbackAction a(*GetLayout());
                    if (!pTableFrame->GetCharRect( m_aCharRect, *m_pTableCursor->GetPoint(), &aTmpState))
                    {
                        Point aCentrPt( m_aCharRect.Center() );
                        aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
                        pTableFrame->GetCursorOfst(m_pTableCursor->GetPoint(), aCentrPt, &aTmpState);
                        bool const bResult =
                            pTableFrame->GetCharRect(m_aCharRect, *m_pTableCursor->GetPoint());
                        OSL_ENSURE( bResult, "GetCharRect failed." );
                    }
                }

                m_pVisibleCursor->Hide(); // always hide visible Cursor
                // scroll Cursor to visible area
                if( eFlags & SwCursorShell::SCROLLWIN &&
                    (HasSelection() || eFlags & SwCursorShell::READONLY ||
                     !IsCursorReadonly()) )
                {
                    SwFrame* pBoxFrame = pTableFrame;
                    while( pBoxFrame && !pBoxFrame->IsCellFrame() )
                        pBoxFrame = pBoxFrame->GetUpper();
                    if( pBoxFrame && pBoxFrame->getFrameArea().HasArea() )
                        MakeVisible( pBoxFrame->getFrameArea() );
                    else
                        MakeVisible( m_aCharRect );
                }

                // let Layout create the Cursors in the Boxes
                if( m_pTableCursor->IsCursorMovedUpdate() )
                    GetLayout()->MakeTableCursors( *m_pTableCursor );
                if( m_bHasFocus && !m_bBasicHideCursor )
                    m_pTableCursor->Show(nullptr);

                // set Cursor-Points to the new Positions
                m_pTableCursor->GetPtPos().setX(m_aCharRect.Left());
                m_pTableCursor->GetPtPos().setY(m_aCharRect.Top());

                if( m_bSVCursorVis )
                {
                    m_aCursorHeight.setX(0);
                    m_aCursorHeight.setY(aTmpState.m_aRealHeight.getY() < 0 ?
                                      -m_aCharRect.Width() : m_aCharRect.Height());
                    m_pVisibleCursor->Show(); // show again
                }
                m_eMvState = MV_NONE;  // state for cursor travelling - GetCursorOfst
                if( pTableFrame && Imp()->IsAccessible() )
                    Imp()->InvalidateAccessibleCursorPosition( pTableFrame );
                return;
            }
        }

        if( m_pTableCursor )
        {
            // delete Ring
            while( m_pCurrentCursor->GetNext() != m_pCurrentCursor )
                delete m_pCurrentCursor->GetNext();
            m_pCurrentCursor->DeleteMark();
            *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();
            m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos();
            delete m_pTableCursor;
            m_pTableCursor = nullptr;
        }

        m_pVisibleCursor->Hide(); // always hide visible Cursor

        // are we perhaps in a protected / hidden Section ?
        {
            SwShellCursor* pShellCursor = getShellCursor( true );
            bool bChgState = true;
            const SwSectionNode* pSectNd = pShellCursor->GetNode().FindSectionNode();
            if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() ||
                ( !IsReadOnlyAvailable() &&
                  pSectNd->GetSection().IsProtectFlag() &&
                 ( !mxDoc->GetDocShell() ||
                   !mxDoc->GetDocShell()->IsReadOnly() || m_bAllProtect )) ) )
            {
                if( !FindValidContentNode( !HasDrawView() ||
                        0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
                {
                    // everything protected/hidden -> special mode
                    if( m_bAllProtect && !IsReadOnlyAvailable() &&
                        pSectNd->GetSection().IsProtectFlag() )
                        bChgState = false;
                    else
                    {
                        m_eMvState = MV_NONE;     // state for cursor travelling
                        m_bAllProtect = true;
                        if( GetDoc()->GetDocShell() )
                        {
                            GetDoc()->GetDocShell()->SetReadOnlyUI();
                            CallChgLnk();       // notify UI!
                        }
                        return;
                    }
                }
            }
            if( bChgState )
            {
                bool bWasAllProtect = m_bAllProtect;
                m_bAllProtect = false;
                if( bWasAllProtect && GetDoc()->GetDocShell() &&
                    GetDoc()->GetDocShell()->IsReadOnlyUI() )
                {
                    GetDoc()->GetDocShell()->SetReadOnlyUI( false );
                    CallChgLnk();       // notify UI!
                }
            }
        }

        UpdateCursorPos();

        // The cursor must always point into content; there's some code
        // that relies on this. (E.g. in SwEditShell::GetScriptType, which always
        // loops _behind_ the last node in the selection, which always works if you
        // are in content.) To achieve this, we'll force cursor(s) to point into
        // content, if UpdateCursorPos() hasn't already done so.
        for(SwPaM& rCmp : m_pCurrentCursor->GetRingContainer())
        {
            // start will move forwards, end will move backwards
            bool bPointIsStart = ( rCmp.Start() == rCmp.GetPoint() );

            // move point; forward if it's the start, backwards if it's the end
            if( ! rCmp.GetPoint()->nNode.GetNode().IsContentNode() )
                rCmp.Move( bPointIsStart ? fnMoveForward : fnMoveBackward,
                            GoInContent );

            // move mark (if exists); forward if it's the start, else backwards
            if( rCmp.HasMark() )
            {
                if( ! rCmp.GetMark()->nNode.GetNode().IsContentNode() )
                {
                    rCmp.Exchange();
                    rCmp.Move( !bPointIsStart ? fnMoveForward : fnMoveBackward,
                                GoInContent );
                    rCmp.Exchange();
                }
            }
        }

        SwRect aOld( m_aCharRect );
        bool bFirst = true;
        SwContentFrame *pFrame;
        int nLoopCnt = 100;
        SwShellCursor* pShellCursor = getShellCursor( true );

        do {
            bool bAgainst;
            do {
                bAgainst = false;
                pFrame = pShellCursor->GetContentNode()->getLayoutFrame( GetLayout(),
                            &pShellCursor->GetPtPos(), pShellCursor->GetPoint(), false );
                // if the Frame doesn't exist anymore, the complete Layout has to be
                // created, because there used to be a Frame here!
                if ( !pFrame )
                {
                    do
                    {
                        CalcLayout();
                        pFrame = pShellCursor->GetContentNode()->getLayoutFrame( GetLayout(),
                                    &pShellCursor->GetPtPos(), pShellCursor->GetPoint(), false );
                    }  while( !pFrame );
                }
                else if ( Imp()->IsIdleAction() )
                    // Guarantee everything's properly formatted
                    pFrame->PrepareCursor();

                // In protected Fly? but ignore in case of frame selection
                if( !IsReadOnlyAvailable() && pFrame->IsProtected() &&
                    ( !Imp()->GetDrawView() ||
                      !Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() ) &&
                    (!mxDoc->GetDocShell() ||
                     !mxDoc->GetDocShell()->IsReadOnly() || m_bAllProtect ) )
                {
                    // look for a valid position
                    bool bChgState = true;
                    if( !FindValidContentNode(!HasDrawView() ||
                        0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
                    {
                        // everything is protected / hidden -> special Mode
                        if( m_bAllProtect )
                            bChgState = false;
                        else
                        {
                            m_eMvState = MV_NONE;     // state for cursor travelling
                            m_bAllProtect = true;
                            if( GetDoc()->GetDocShell() )
                            {
                                GetDoc()->GetDocShell()->SetReadOnlyUI();
                                CallChgLnk();       // notify UI!
                            }
                            return;
                        }
                    }

                    if( bChgState )
                    {
                        bool bWasAllProtect = m_bAllProtect;
                        m_bAllProtect = false;
                        if( bWasAllProtect && GetDoc()->GetDocShell() &&
                            GetDoc()->GetDocShell()->IsReadOnlyUI() )
                        {
                            GetDoc()->GetDocShell()->SetReadOnlyUI( false );
                            CallChgLnk();       // notify UI!
                        }
                        m_bAllProtect = false;
                        bAgainst = true; // look for the right Frame again
                    }
                }
            } while( bAgainst );

            SwCursorMoveState aTmpState( m_eMvState );
            aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
            aTmpState.m_bRealHeight = true;
            aTmpState.m_bRealWidth = IsOverwriteCursor();
            aTmpState.m_nCursorBidiLevel = pShellCursor->GetCursorBidiLevel();

            // #i27615#,#i30453#
            SwSpecialPos aSpecialPos;
            aSpecialPos.nExtendRange = SwSPExtendRange::BEFORE;
            if (pShellCursor->IsInFrontOfLabel())
            {
                aTmpState.m_pSpecialPos = &aSpecialPos;
            }

            {
                DisableCallbackAction a(*GetLayout()); // tdf#91602 prevent recursive Action
                if (!pFrame->GetCharRect(m_aCharRect, *pShellCursor->GetPoint(), &aTmpState))
                {
                    Point& rPt = pShellCursor->GetPtPos();
                    rPt = m_aCharRect.Center();
                    pFrame->GetCursorOfst( pShellCursor->GetPoint(), rPt, &aTmpState );
                }
            }
            UISizeNotify(); // tdf#96256 update view size

            if( !pShellCursor->HasMark() )
                m_aCursorHeight = aTmpState.m_aRealHeight;
            else
            {
                m_aCursorHeight.setX(0);
                m_aCursorHeight.setY(aTmpState.m_aRealHeight.getY() < 0 ?
                                  -m_aCharRect.Width() : m_aCharRect.Height());
            }

            if( !bFirst && aOld == m_aCharRect )
                break;

            // if the layout says that we are after the 100th iteration still in
            // flow then we should always take the current position for granted.
            // (see bug: 29658)
            if( !--nLoopCnt )
            {
                OSL_ENSURE( false, "endless loop? CharRect != OldCharRect ");
                break;
            }
            aOld = m_aCharRect;
            bFirst = false;

            // update cursor Points to the new Positions
            pShellCursor->GetPtPos().setX(m_aCharRect.Left());
            pShellCursor->GetPtPos().setY(m_aCharRect.Top());

            if( !(eFlags & SwCursorShell::UPDOWN ))   // delete old Pos. of Up/Down
            {
                DisableCallbackAction a(*GetLayout());
                pFrame->Calc(GetOut());
                m_nUpDownX = pFrame->IsVertical() ?
                           m_aCharRect.Top() - pFrame->getFrameArea().Top() :
                           m_aCharRect.Left() - pFrame->getFrameArea().Left();
            }

            // scroll Cursor to visible area
            if( m_bHasFocus && eFlags & SwCursorShell::SCROLLWIN &&
                (HasSelection() || eFlags & SwCursorShell::READONLY ||
                 !IsCursorReadonly() || GetViewOptions()->IsSelectionInReadonly()) )
            {
                // in case of scrolling this EndAction doesn't show the SV cursor
                // again, thus save and reset the flag here
                bool bSav = m_bSVCursorVis;
                m_bSVCursorVis = false;
                MakeSelVisible();
                m_bSVCursorVis = bSav;
            }

        } while( eFlags & SwCursorShell::SCROLLWIN );

        if( m_pBlockCursor )
            RefreshBlockCursor();

        // We should not restrict cursor update to the active view when using LOK
        bool bCheckFocus = m_bHasFocus || comphelper::LibreOfficeKit::isActive();

        if( !bIdleEnd && bCheckFocus && !m_bBasicHideCursor )
        {
            if( m_pTableCursor )
                m_pTableCursor->SwSelPaintRects::Show();
            else
            {
                m_pCurrentCursor->SwSelPaintRects::Show();
                if( m_pBlockCursor )
                {
                    SwShellCursor* pNxt = m_pCurrentCursor->GetNext();
                    while( pNxt && pNxt != m_pCurrentCursor )
                    {
                        pNxt->SwSelPaintRects::Show();
                        pNxt = pNxt->GetNext();
                    }
                }
            }
        }

        m_eMvState = MV_NONE; // state for cursor travelling - GetCursorOfst

        if( pFrame && Imp()->IsAccessible() )
            Imp()->InvalidateAccessibleCursorPosition( pFrame );

        // switch from blinking cursor to read-only-text-selection cursor
        const sal_uInt64 nBlinkTime = GetOut()->GetSettings().GetStyleSettings().
                                GetCursorBlinkTime();

        if ( (IsCursorReadonly() && GetViewOptions()->IsSelectionInReadonly()) ==
            ( nBlinkTime != STYLE_CURSOR_NOBLINKTIME ) )
        {
            // non blinking cursor in read only - text selection mode
            AllSettings aSettings = GetOut()->GetSettings();
            StyleSettings aStyleSettings = aSettings.GetStyleSettings();
            const sal_uInt64 nNewBlinkTime = nBlinkTime == STYLE_CURSOR_NOBLINKTIME ?
                                       Application::GetSettings().GetStyleSettings().GetCursorBlinkTime() :
                                       STYLE_CURSOR_NOBLINKTIME;
            aStyleSettings.SetCursorBlinkTime( nNewBlinkTime );
            aSettings.SetStyleSettings( aStyleSettings );
            GetOut()->SetSettings( aSettings );
        }

        if( m_bSVCursorVis )
            m_pVisibleCursor->Show(); // show again
    }

    void SwCursorShell::RefreshBlockCursor()
    {
        assert(m_pBlockCursor);
        SwShellCursor &rBlock = m_pBlockCursor->getShellCursor();
        Point aPt = rBlock.GetPtPos();
        SwContentFrame* pFrame = rBlock.GetContentNode()->getLayoutFrame( GetLayout(), &aPt, rBlock.GetPoint(), false );
        Point aMk;
        if( m_pBlockCursor->getEndPoint() && m_pBlockCursor->getStartPoint() )
        {
            aPt = *m_pBlockCursor->getStartPoint();
            aMk = *m_pBlockCursor->getEndPoint();
        }
        else
        {
            aPt = rBlock.GetPtPos();
            if( pFrame )
            {
                if( pFrame->IsVertical() )
                    aPt.setY(pFrame->getFrameArea().Top() + GetUpDownX());
                else
                    aPt.setX(pFrame->getFrameArea().Left() + GetUpDownX());
            }
            aMk = rBlock.GetMkPos();
        }
        SwRect aRect( aMk, aPt );
        aRect.Justify();
        SwSelectionList aSelList( pFrame );

        if( GetLayout()->FillSelection( aSelList, aRect ) )
        {
            SwCursor* pNxt = static_cast<SwCursor*>(m_pCurrentCursor->GetNext());
            while( pNxt != m_pCurrentCursor )
            {
                delete pNxt;
                pNxt = static_cast<SwCursor*>(m_pCurrentCursor->GetNext());
            }

            std::list<SwPaM*>::iterator pStart = aSelList.getStart();
            std::list<SwPaM*>::iterator pPam = aSelList.getEnd();
            OSL_ENSURE( pPam != pStart, "FillSelection should deliver at least one PaM" );
            m_pCurrentCursor->SetMark();
            --pPam;
            // If there is only one text portion inside the rectangle, a simple
            // selection is created
            if( pPam == pStart )
            {
                *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint();
                if( (*pPam)->HasMark() )
                    *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
                else
                    m_pCurrentCursor->DeleteMark();
                delete *pPam;
                m_pCurrentCursor->SetColumnSelection( false );
            }
            else
            {
                // The order of the SwSelectionList has to be preserved but
                // the order inside the ring created by CreateCursor() is not like
                // expected => First create the selections before the last one
                // downto the first selection.
                // At least create the cursor for the last selection
                --pPam;
                *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n-1 (if n == number of selections)
                if( (*pPam)->HasMark() )
                    *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
                else
                    m_pCurrentCursor->DeleteMark();
                delete *pPam;
                m_pCurrentCursor->SetColumnSelection( true );
                while( pPam != pStart )
                {
                    --pPam;

                    SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor );
                    pNew->insert( pNew->begin(), m_pCurrentCursor->begin(),  m_pCurrentCursor->end());
                    m_pCurrentCursor->clear();
                    m_pCurrentCursor->DeleteMark();

                    *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n-2, n-3, .., 2, 1
                    if( (*pPam)->HasMark() )
                    {
                        m_pCurrentCursor->SetMark();
                        *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
                    }
                    else
                        m_pCurrentCursor->DeleteMark();
                    m_pCurrentCursor->SetColumnSelection( true );
                    delete *pPam;
                }
                {
                    SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor );
                    pNew->insert( pNew->begin(), m_pCurrentCursor->begin(), m_pCurrentCursor->end() );
                    m_pCurrentCursor->clear();
                    m_pCurrentCursor->DeleteMark();
                }
                pPam = aSelList.getEnd();
                --pPam;
                *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n, the last selection
                if( (*pPam)->HasMark() )
                {
                    m_pCurrentCursor->SetMark();
                    *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
                }
                else
                    m_pCurrentCursor->DeleteMark();
                m_pCurrentCursor->SetColumnSelection( true );
                delete *pPam;
            }
        }
    }

    /// create a copy of the cursor and save it in the stack
    void SwCursorShell::Push()
    {
        // fdo#60513: if we have a table cursor, copy that; else copy current.
        // This seems to work because UpdateCursor() will fix this up on Pop(),
        // then MakeBoxSels() will re-create the current m_pCurrentCursor cell ring.
        SwShellCursor *const pCurrent((m_pTableCursor) ? m_pTableCursor : m_pCurrentCursor);
        m_pStackCursor = new SwShellCursor( *this, *pCurrent->GetPoint(),
                                        pCurrent->GetPtPos(), m_pStackCursor );

        if (pCurrent->HasMark())
        {
            m_pStackCursor->SetMark();
            *m_pStackCursor->GetMark() = *pCurrent->GetMark();
        }
    }

    /** delete cursor

        @param eDelete  delete from stack, or delete current
                        and assign the one from stack as the new current cursor.
        @return <true> if there was one on the stack, <false> otherwise
    */
    bool SwCursorShell::Pop(PopMode const eDelete)
    {
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed

        // are there any left?
        if (nullptr == m_pStackCursor)
            return false;

        SwShellCursor *pTmp = nullptr, *pOldStack = m_pStackCursor;

        // the successor becomes the current one
        if (m_pStackCursor->GetNext() != m_pStackCursor)
        {
            pTmp = m_pStackCursor->GetNext();
        }

        if (PopMode::DeleteStack == eDelete)
            delete m_pStackCursor;

        m_pStackCursor = pTmp; // assign new one

        if (PopMode::DeleteCurrent == eDelete)
        {
            SwCursorSaveState aSaveState( *m_pCurrentCursor );

            // If the visible SSelection was not changed
            const Point& rPoint = pOldStack->GetPtPos();
            if (rPoint == m_pCurrentCursor->GetPtPos() || rPoint == m_pCurrentCursor->GetMkPos())
            {
                // move "Selections Rectangles"
                m_pCurrentCursor->insert( m_pCurrentCursor->begin(), pOldStack->begin(), pOldStack->end() );
                pOldStack->clear();
            }

            if( pOldStack->HasMark() )
            {
                m_pCurrentCursor->SetMark();
                *m_pCurrentCursor->GetMark() = *pOldStack->GetMark();
                m_pCurrentCursor->GetMkPos() = pOldStack->GetMkPos();
            }
            else
                // no selection so revoke old one and set to old position
                m_pCurrentCursor->DeleteMark();
            *m_pCurrentCursor->GetPoint() = *pOldStack->GetPoint();
            m_pCurrentCursor->GetPtPos() = pOldStack->GetPtPos();
            delete pOldStack;

            if( !m_pCurrentCursor->IsInProtectTable( true ) &&
                !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
                                     SwCursorSelOverFlags::ChangePos ) )
            {
                UpdateCursor(); // update current cursor
                if (m_pTableCursor)
                { // tdf#106929 ensure m_pCurrentCursor ring is recreated from table
                    m_pTableCursor->SetChgd();
                }
            }
        }
        return true;
    }

    /** Combine two cursors

        Delete topmost from stack and use its GetMark in the current.
    */
    void SwCursorShell::Combine()
    {
        // any others left?
        if (nullptr == m_pStackCursor)
            return;

        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        // rhbz#689053: IsSelOvr must restore the saved stack position, not the
        // current one, because current point + stack mark may be invalid PaM
        SwCursorSaveState aSaveState(*m_pStackCursor);
        // stack cursor & current cursor in same Section?
        assert(!m_pStackCursor->HasMark() ||
                CheckNodesRange(m_pStackCursor->GetMark()->nNode,
                                m_pCurrentCursor->GetPoint()->nNode, true));
        *m_pStackCursor->GetPoint() = *m_pCurrentCursor->GetPoint();
        m_pStackCursor->GetPtPos() = m_pCurrentCursor->GetPtPos();

        SwShellCursor * pTmp = nullptr;
        if (m_pStackCursor->GetNext() != m_pStackCursor)
        {
            pTmp = m_pStackCursor->GetNext();
        }
        delete m_pCurrentCursor;
        m_pCurrentCursor = m_pStackCursor;
        m_pStackCursor->MoveTo(nullptr); // remove from ring
        m_pStackCursor = pTmp;
        if( !m_pCurrentCursor->IsInProtectTable( true ) &&
            !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
                                 SwCursorSelOverFlags::ChangePos ) )
        {
            UpdateCursor(); // update current cursor
        }
    }

    void SwCursorShell::HideCursors()
    {
        if( !m_bHasFocus || m_bBasicHideCursor )
            return;

        // if cursor is visible then hide SV cursor
        if( m_pVisibleCursor->IsVisible() )
        {
            SET_CURR_SHELL( this );
            m_pVisibleCursor->Hide();
        }
        // revoke inversion of SSelection
        SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
        pCurrentCursor->Hide();
    }

    void SwCursorShell::ShowCursors( bool bCursorVis )
    {
        if( !m_bHasFocus || m_bAllProtect || m_bBasicHideCursor )
            return;

        SET_CURR_SHELL( this );
        SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
        pCurrentCursor->Show(nullptr);

        if( m_bSVCursorVis && bCursorVis ) // also show SV cursor again
            m_pVisibleCursor->Show();
    }

    void SwCursorShell::ShowCursor()
    {
        if( !m_bBasicHideCursor )
        {
            m_bSVCursorVis = true;
            m_pCurrentCursor->SetShowTextInputFieldOverlay( true );

            if (comphelper::LibreOfficeKit::isActive())
            {
                OString aPayload = OString::boolean(m_bSVCursorVis);
                GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload.getStr());
                SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload);
            }

            UpdateCursor();
        }
    }

    void SwCursorShell::HideCursor()
    {
        if( !m_bBasicHideCursor )
        {
            m_bSVCursorVis = false;
            // possibly reverse selected areas!!
            SET_CURR_SHELL( this );
            m_pCurrentCursor->SetShowTextInputFieldOverlay( false );
            m_pVisibleCursor->Hide();

            if (comphelper::LibreOfficeKit::isActive())
            {
                OString aPayload = OString::boolean(m_bSVCursorVis);
                GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload.getStr());
                SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload);
            }
        }
    }

    void SwCursorShell::ShellLoseFocus()
    {
        if( !m_bBasicHideCursor )
            HideCursors();
        m_bHasFocus = false;
    }

    void SwCursorShell::ShellGetFocus()
    {
        m_bHasFocus = true;
        if( !m_bBasicHideCursor && VisArea().Width() )
        {
            UpdateCursor( static_cast<sal_uInt16>( SwCursorShell::CHKRANGE ) );
            ShowCursors( m_bSVCursorVis );
        }
    }

    /** Get current frame in which the cursor is positioned. */
    SwContentFrame *SwCursorShell::GetCurrFrame( const bool bCalcFrame ) const
    {
        SET_CURR_SHELL( const_cast<SwCursorShell*>(this) );
        SwContentFrame *pRet = nullptr;
        SwContentNode *pNd = m_pCurrentCursor->GetContentNode();
        if ( pNd )
        {
            if ( bCalcFrame )
            {
                sal_uInt16* pST = const_cast<sal_uInt16*>(&mnStartAction);
                ++(*pST);
                const Size aOldSz( GetDocSize() );
                pRet = pNd->getLayoutFrame( GetLayout(), &m_pCurrentCursor->GetPtPos(), m_pCurrentCursor->GetPoint() );
                --(*pST);
                if( aOldSz != GetDocSize() )
                    const_cast<SwCursorShell*>(this)->SizeChgNotify();
            }
            else
                pRet = pNd->getLayoutFrame( GetLayout(), &m_pCurrentCursor->GetPtPos(), m_pCurrentCursor->GetPoint(), false);
        }
        return pRet;
    }

    //TODO: provide documentation
    /** forward all attribute/format changes at the current node to the Link

        @param pOld ???
        @param pNew ???
    */
    void SwCursorShell::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
    {
        const sal_uInt16 nWhich = pOld ?
                              pOld->Which() :
                              pNew ?
                              pNew->Which() :
                              sal::static_int_cast<sal_uInt16>(RES_MSG_BEGIN);

        if( m_bCallChgLnk &&
            ( nWhich < RES_MSG_BEGIN || nWhich >= RES_MSG_END ||
                nWhich == RES_FMT_CHG || nWhich == RES_UPDATE_ATTR ||
                nWhich == RES_ATTRSET_CHG ))
            // messages are not forwarded
            // #i6681#: RES_UPDATE_ATTR is implicitly unset in
            // SwTextNode::Insert(SwTextHint*, sal_uInt16); we react here and thus do
            // not need to send the expensive RES_FMT_CHG in Insert.
            CallChgLnk();

        if( m_aGrfArrivedLnk.IsSet() &&
            ( RES_GRAPHIC_ARRIVED == nWhich || RES_GRAPHIC_SWAPIN == nWhich ))
            m_aGrfArrivedLnk.Call( *this );
    }

    /** Does the current cursor create a selection?

        This means checking if GetMark is set and if SPoint and GetMark differ.
    */
    bool SwCursorShell::HasSelection() const
    {
        const SwPaM* pCursor = getShellCursor( true );
        return IsTableMode() || ( pCursor->HasMark() && *pCursor->GetPoint() != *pCursor->GetMark() );
    }

    void SwCursorShell::CallChgLnk()
    {
        // Do not make any call in StartAction/EndAction but just set the flag.
        // This will be handled in EndAction.
        if (ActionPend())
            m_bChgCallFlag = true; // remember change
        else if( m_aChgLnk.IsSet() )
        {
            if( m_bCallChgLnk )
                m_aChgLnk.Call( this );
            m_bChgCallFlag = false; // reset flag
        }
    }

    /// get selected text of a node at current cursor
    OUString SwCursorShell::GetSelText() const
    {
        OUString aText;
        if( m_pCurrentCursor->GetPoint()->nNode.GetIndex() ==
            m_pCurrentCursor->GetMark()->nNode.GetIndex() )
        {
            SwTextNode* pTextNd = m_pCurrentCursor->GetNode().GetTextNode();
            if( pTextNd )
            {
                const sal_Int32 nStt = m_pCurrentCursor->Start()->nContent.GetIndex();
                aText = pTextNd->GetExpandText( nStt,
                        m_pCurrentCursor->End()->nContent.GetIndex() - nStt );
            }
        }
        return aText;
    }

    /// get text only from current cursor position (until end of node)
    OUString SwCursorShell::GetText() const
    {
        OUString aText;
        if( m_pCurrentCursor->GetPoint()->nNode.GetIndex() ==
            m_pCurrentCursor->GetMark()->nNode.GetIndex() )
        {
            SwTextNode* pTextNd = m_pCurrentCursor->GetNode().GetTextNode();
            if( pTextNd )
                aText = pTextNd->GetText().copy(
                        m_pCurrentCursor->GetPoint()->nContent.GetIndex() );
        }
        return aText;
    }

    /** get the nth character of the current SSelection

        @param bEnd    Start counting from the end? From start otherwise.
        @param nOffset position of the character
    */
    sal_Unicode SwCursorShell::GetChar( bool bEnd, long nOffset )
    {
        if( IsTableMode() ) // not possible in table mode
            return 0;

        const SwPosition* pPos = !m_pCurrentCursor->HasMark() ? m_pCurrentCursor->GetPoint()
                                    : bEnd ? m_pCurrentCursor->End() : m_pCurrentCursor->Start();
        SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
        if( !pTextNd )
            return 0;

        const sal_Int32 nPos = pPos->nContent.GetIndex();
        const OUString& rStr = pTextNd->GetText();
        sal_Unicode cCh = 0;

        if (((nPos+nOffset) >= 0 ) && (nPos+nOffset) < rStr.getLength())
            cCh = rStr[nPos + nOffset];

        return cCh;
    }

    /** extend current SSelection by n characters

        @param bEnd   Start counting from the end? From start otherwise.
        @param nCount Number of characters.
    */
    bool SwCursorShell::ExtendSelection( bool bEnd, sal_Int32 nCount )
    {
        if( !m_pCurrentCursor->HasMark() || IsTableMode() )
            return false; // no selection

        SwPosition* pPos = bEnd ? m_pCurrentCursor->End() : m_pCurrentCursor->Start();
        SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
        assert(pTextNd);

        sal_Int32 nPos = pPos->nContent.GetIndex();
        if( bEnd )
        {
            if ((nPos + nCount) <= pTextNd->GetText().getLength())
                nPos = nPos + nCount;
            else
                return false; // not possible
        }
        else if( nPos >= nCount )
            nPos = nPos - nCount;
        else
            return false; // not possible anymore

        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed

        pPos->nContent = nPos;
        UpdateCursor();

        return true;
    }

    /** Move visible cursor to given position in document.

        @param rPt The position to move the visible cursor to.
        @return <false> if SPoint was corrected by the layout.
    */
    bool SwCursorShell::SetVisibleCursor( const Point &rPt )
    {
        SET_CURR_SHELL( this );
        Point aPt( rPt );
        SwPosition aPos( *m_pCurrentCursor->GetPoint() );
        SwCursorMoveState aTmpState( MV_SETONLYTEXT );
        aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
        aTmpState.m_bRealHeight = true;

        const bool bRet = GetLayout()->GetCursorOfst( &aPos, aPt /*, &aTmpState*/ );

        SetInFrontOfLabel( false ); // #i27615#

        // show only in TextNodes
        SwTextNode* pTextNd = aPos.nNode.GetNode().GetTextNode();
        if( !pTextNd )
            return false;

        const SwSectionNode* pSectNd = pTextNd->FindSectionNode();
        if( pSectNd && (pSectNd->GetSection().IsHiddenFlag() ||
                        ( !IsReadOnlyAvailable() &&
                          pSectNd->GetSection().IsProtectFlag())) )
            return false;

        SwContentFrame *pFrame = pTextNd->getLayoutFrame( GetLayout(), &aPt, &aPos );
        if ( Imp()->IsIdleAction() )
            pFrame->PrepareCursor();
        SwRect aTmp( m_aCharRect );

        pFrame->GetCharRect( m_aCharRect, aPos, &aTmpState );

        // #i10137#
        if( aTmp == m_aCharRect && m_pVisibleCursor->IsVisible() )
            return true;

        m_pVisibleCursor->Hide(); // always hide visible cursor
        if( IsScrollMDI( this, m_aCharRect ))
        {
            MakeVisible( m_aCharRect );
            m_pCurrentCursor->Show(nullptr);
        }

        {
            if( aTmpState.m_bRealHeight )
                m_aCursorHeight = aTmpState.m_aRealHeight;
            else
            {
                m_aCursorHeight.setX(0);
                m_aCursorHeight.setY(m_aCharRect.Height());
            }

            m_pVisibleCursor->SetDragCursor();
            m_pVisibleCursor->Show(); // show again
        }
        return bRet;
    }

    bool SwCursorShell::IsOverReadOnlyPos( const Point& rPt ) const
    {
        Point aPt( rPt );
        SwPaM aPam( *m_pCurrentCursor->GetPoint() );
        GetLayout()->GetCursorOfst( aPam.GetPoint(), aPt );
        // form view
        return aPam.HasReadonlySel( GetViewOptions()->IsFormView() );
    }

    /** Get the number of elements in the ring of cursors

        @param bAll If <false> get only spanned ones (= with selections) (Basic).
    */
    sal_uInt16 SwCursorShell::GetCursorCnt( bool bAll ) const
    {
        SwPaM* pTmp = GetCursor()->GetNext();
        sal_uInt16 n = (bAll || ( m_pCurrentCursor->HasMark() &&
                        *m_pCurrentCursor->GetPoint() != *m_pCurrentCursor->GetMark())) ? 1 : 0;
        while( pTmp != m_pCurrentCursor )
        {
            if( bAll || ( pTmp->HasMark() &&
                    *pTmp->GetPoint() != *pTmp->GetMark()))
                ++n;
            pTmp = pTmp->GetNext();
        }
        return n;
    }

    bool SwCursorShell::IsStartOfDoc() const
    {
        if( m_pCurrentCursor->GetPoint()->nContent.GetIndex() )
            return false;

        // after EndOfIcons comes the content selection (EndNd+StNd+ContentNd)
        SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfExtras(), 2 );
        if( !aIdx.GetNode().IsContentNode() )
            GetDoc()->GetNodes().GoNext( &aIdx );
        return aIdx == m_pCurrentCursor->GetPoint()->nNode;
    }

    bool SwCursorShell::IsEndOfDoc() const
    {
        SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfContent(), -1 );
        SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
        if( !pCNd )
            pCNd = SwNodes::GoPrevious( &aIdx );

        return aIdx == m_pCurrentCursor->GetPoint()->nNode &&
                pCNd->Len() == m_pCurrentCursor->GetPoint()->nContent.GetIndex();
    }

    /** Invalidate cursors

        Delete all created cursors, set table crsr and last crsr to their TextNode
        (or StartNode?). They will then all re-created at the next ::GetCursor() call.

        This is needed for Drag&Drop/ Clipboard-paste in tables.
    */
    bool SwCursorShell::ParkTableCursor()
    {
        if( !m_pTableCursor )
            return false;

        m_pTableCursor->ParkCursor();

        while( m_pCurrentCursor->GetNext() != m_pCurrentCursor )
            delete m_pCurrentCursor->GetNext();

        // *always* move cursor's Point and Mark
        m_pCurrentCursor->DeleteMark();
        *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();

        return true;
    }

    void SwCursorShell::ParkPams( SwPaM* pDelRg, SwShellCursor** ppDelRing )
    {
        const SwPosition *pStt = pDelRg->Start(),
            *pEnd = pDelRg->GetPoint() == pStt ? pDelRg->GetMark() : pDelRg->GetPoint();

        SwPaM *pTmpDel = nullptr, *pTmp = *ppDelRing;

        // search over the whole ring
        bool bGoNext;
        do {

            if (!pTmp)
                break;

            const SwPosition *pTmpStt = pTmp->Start(),
                            *pTmpEnd = pTmp->GetPoint() == pTmpStt ?
                                            pTmp->GetMark() : pTmp->GetPoint();
            // If a SPoint or GetMark are in a cursor area than cancel the old area.
            // During comparison keep in mind that End() is outside the area.
            if( *pStt <= *pTmpStt )
            {
                if( *pEnd > *pTmpStt ||
                    ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd ))
                    pTmpDel = pTmp;
            }
            else
                if( *pStt < *pTmpEnd )
                    pTmpDel = pTmp;

            bGoNext = true;
            if (pTmpDel) // is the pam in the range -> delete
            {
                bool bDelete = true;
                if( *ppDelRing == pTmpDel )
                {
                    if( *ppDelRing == m_pCurrentCursor )
                    {
                        bDelete = GoNextCursor();
                        if( bDelete )
                        {
                            bGoNext = false;
                            pTmp = pTmp->GetNext();
                        }
                    }
                    else
                        bDelete = false; // never delete the StackCursor
                }

                if( bDelete )
                {
                    if (pTmp == pTmpDel)
                        pTmp = nullptr;
                    delete pTmpDel; // invalidate old area
                }
                else
                {
                    pTmpDel->GetPoint()->nContent.Assign(nullptr, 0);
                    pTmpDel->GetPoint()->nNode = 0;
                    pTmpDel->DeleteMark();
                }
                pTmpDel = nullptr;
            }
            if( bGoNext && pTmp )
                pTmp = pTmp->GetNext();

        } while( !bGoNext || *ppDelRing != pTmp );
    }

    //TODO: provide documentation
    /** Remove selections and additional cursors of all shells.

        The remaining cursor of the shell is parked.

        @param rIdx ???
    */
    void SwCursorShell::ParkCursor( const SwNodeIndex &rIdx )
    {
        SwNode *pNode = &rIdx.GetNode();

        // create a new PaM
        std::unique_ptr<SwPaM> pNew( new SwPaM( *GetCursor()->GetPoint() ) );
        if( pNode->GetStartNode() )
        {
            if( ( pNode = pNode->StartOfSectionNode())->IsTableNode() )
            {
                // the given node is in a table, thus park cursor to table node
                // (outside of the table)
                pNew->GetPoint()->nNode = *pNode->StartOfSectionNode();
            }
            else
                // Also on the start node itself. Then we need to request the start
                // node always via its end node! (StartOfSelection of StartNode is
                // the parent)
                pNew->GetPoint()->nNode = *pNode->EndOfSectionNode()->StartOfSectionNode();
        }
        else
            pNew->GetPoint()->nNode = *pNode->StartOfSectionNode();
        pNew->SetMark();
        pNew->GetPoint()->nNode = *pNode->EndOfSectionNode();

        // take care of all shells
        for(SwViewShell& rTmp : GetRingContainer())
        {
            if( dynamic_cast<const SwCursorShell *>(&rTmp) != nullptr)
            {
                SwCursorShell* pSh = static_cast<SwCursorShell*>(&rTmp);
                if (pSh->m_pStackCursor)
                    pSh->ParkPams(pNew.get(), &pSh->m_pStackCursor);

                pSh->ParkPams( pNew.get(), &pSh->m_pCurrentCursor );
                if( pSh->m_pTableCursor )
                {
                    // set table cursor always to 0 and the current one always to
                    // the beginning of the table
                    SwPaM* pTCursor = pSh->GetTableCrs();
                    SwNode* pTableNd = pTCursor->GetPoint()->nNode.GetNode().FindTableNode();
                    if ( pTableNd )
                    {
                        pTCursor->GetPoint()->nContent.Assign(nullptr, 0);
                        pTCursor->GetPoint()->nNode = 0;
                        pTCursor->DeleteMark();
                        pSh->m_pCurrentCursor->GetPoint()->nNode = *pTableNd;
                    }
                }
            }
        }
    }

    /** Copy constructor

        Copy cursor position and add it to the ring.
        All views of a document are in the ring of the shell.
    */
    SwCursorShell::SwCursorShell( SwCursorShell& rShell, vcl::Window *pInitWin )
        : SwViewShell( rShell, pInitWin )
        , SwModify( nullptr )
        , m_pStackCursor( nullptr )
        , m_pBlockCursor( nullptr )
        , m_pTableCursor( nullptr )
        , m_pBoxIdx( nullptr )
        , m_pBoxPtr( nullptr )
        , m_nUpDownX(0)
        , m_nLeftFramePos(0)
        , m_nCurrentNode(0)
        , m_nCurrentContent(0)
        , m_nCurrentNdTyp(SwNodeType::NONE)
        , m_nCursorMove( 0 )
        , m_eMvState( MV_NONE )
        , m_sMarkedListId()
        , m_nMarkedListLevel( 0 )
        , m_oldColFrame(nullptr)
    {
        SET_CURR_SHELL( this );
        // only keep the position of the current cursor of the copy shell
        m_pCurrentCursor = new SwShellCursor( *this, *(rShell.m_pCurrentCursor->GetPoint()) );
        m_pCurrentCursor->GetContentNode()->Add( this );

        m_bAllProtect = m_bVisPortChgd = m_bChgCallFlag = m_bInCMvVisportChgd =
        m_bGCAttr = m_bIgnoreReadonly = m_bSelTableCells = m_bBasicHideCursor =
        m_bOverwriteCursor = false;
        m_bCallChgLnk = m_bHasFocus = m_bAutoUpdateCells = true;
        m_bSVCursorVis = true;
        m_bSetCursorInReadOnly = true;
        m_pVisibleCursor = new SwVisibleCursor( this );
        m_bMacroExecAllowed = rShell.IsMacroExecAllowed();
    }

    /// default constructor
    SwCursorShell::SwCursorShell( SwDoc& rDoc, vcl::Window *pInitWin,
                                const SwViewOption *pInitOpt )
        : SwViewShell( rDoc, pInitWin, pInitOpt )
        , SwModify( nullptr )
        , m_pStackCursor( nullptr )
        , m_pBlockCursor( nullptr )
        , m_pTableCursor( nullptr )
        , m_pBoxIdx( nullptr )
        , m_pBoxPtr( nullptr )
        , m_nUpDownX(0)
        , m_nLeftFramePos(0)
        , m_nCurrentNode(0)
        , m_nCurrentContent(0)
        , m_nCurrentNdTyp(SwNodeType::NONE)
        , m_nCursorMove( 0 )
        , m_eMvState( MV_NONE ) // state for crsr-travelling - GetCursorOfst
        , m_sMarkedListId()
        , m_nMarkedListLevel( 0 )
        , m_oldColFrame(nullptr)
    {
        SET_CURR_SHELL( this );
        // create initial cursor and set it to first content position
        SwNodes& rNds = rDoc.GetNodes();

        SwNodeIndex aNodeIdx( *rNds.GetEndOfContent().StartOfSectionNode() );
        SwContentNode* pCNd = rNds.GoNext( &aNodeIdx ); // go to the first ContentNode

        m_pCurrentCursor = new SwShellCursor( *this, SwPosition( aNodeIdx, SwIndex( pCNd, 0 )));

        // Register shell as dependent at current node. As a result all attribute
        // changes can be forwarded via the Link.
        pCNd->Add( this );

        m_bAllProtect = m_bVisPortChgd = m_bChgCallFlag = m_bInCMvVisportChgd =
        m_bGCAttr = m_bIgnoreReadonly = m_bSelTableCells = m_bBasicHideCursor =
        m_bOverwriteCursor = false;
        m_bCallChgLnk = m_bHasFocus = m_bAutoUpdateCells = true;
        m_bSVCursorVis = true;
        m_bSetCursorInReadOnly = true;

        m_pVisibleCursor = new SwVisibleCursor( this );
        m_bMacroExecAllowed = true;
    }

    SwCursorShell::~SwCursorShell()
    {
        // if it is not the last view then at least the field should be updated
        if( !unique() )
            CheckTableBoxContent( m_pCurrentCursor->GetPoint() );
        else
            ClearTableBoxContent();

        delete m_pVisibleCursor;
        delete m_pBlockCursor;
        delete m_pTableCursor;

        // release cursors
        while(m_pCurrentCursor->GetNext() != m_pCurrentCursor)
            delete m_pCurrentCursor->GetNext();
        delete m_pCurrentCursor;

        // free stack
        if (m_pStackCursor)
        {
            while (m_pStackCursor->GetNext() != m_pStackCursor)
                delete m_pStackCursor->GetNext();
            delete m_pStackCursor;
        }

        // #i54025# - do not give a HTML parser that might potentially hang as
        // a client at the cursor shell the chance to hang itself on a TextNode
        EndListeningAll();
    }

    SwShellCursor* SwCursorShell::getShellCursor( bool bBlock )
    {
        if( m_pTableCursor )
            return m_pTableCursor;
        if( m_pBlockCursor && bBlock )
            return &m_pBlockCursor->getShellCursor();
        return m_pCurrentCursor;
    }

    /** Should WaitPtr be switched on for the clipboard?

        Wait for TableMode, multiple selections and more than x selected paragraphs.
    */
    bool SwCursorShell::ShouldWait() const
    {
        if ( IsTableMode() || GetCursorCnt() > 1 )
            return true;

        if( HasDrawView() && GetDrawView()->GetMarkedObjectList().GetMarkCount() )
            return true;

        SwPaM* pPam = GetCursor();
        return pPam->Start()->nNode.GetIndex() + 10 <
                pPam->End()->nNode.GetIndex();
    }

    size_t SwCursorShell::UpdateTableSelBoxes()
    {
        if (m_pTableCursor && (m_pTableCursor->IsChgd() || !m_pTableCursor->GetSelectedBoxesCount()))
        {
             GetLayout()->MakeTableCursors( *m_pTableCursor );
        }
        return (m_pTableCursor) ? m_pTableCursor->GetSelectedBoxesCount() : 0;
    }

    /// show the current selected "object"
    void SwCursorShell::MakeSelVisible()
    {
        OSL_ENSURE( m_bHasFocus, "no focus but cursor should be made visible?" );
        if( m_aCursorHeight.Y() < m_aCharRect.Height() && m_aCharRect.Height() > VisArea().Height() )
        {
            SwRect aTmp( m_aCharRect );
            long nDiff = m_aCharRect.Height() - VisArea().Height();
            if( nDiff < m_aCursorHeight.getX() )
                aTmp.Top( nDiff + m_aCharRect.Top() );
            else
            {
                aTmp.Top( m_aCursorHeight.getX() + m_aCharRect.Top() );
                aTmp.Height( m_aCursorHeight.getY() );
            }
            if( !aTmp.HasArea() )
            {
                aTmp.SSize().AdjustHeight(1 );
                aTmp.SSize().AdjustWidth(1 );
            }
            MakeVisible( aTmp );
        }
        else
        {
            if( m_aCharRect.HasArea() )
                MakeVisible( m_aCharRect );
            else
            {
                SwRect aTmp( m_aCharRect );
                aTmp.SSize().AdjustHeight(1 ); aTmp.SSize().AdjustWidth(1 );
                MakeVisible( aTmp );
            }
        }
    }

    /// search a valid content position (not protected/hidden)
    bool SwCursorShell::FindValidContentNode( bool bOnlyText )
    {
        if( m_pTableCursor )
        {
            assert(!"Did not remove table selection!");
            return false;
        }

        // #i45129# - everything is allowed in UI-readonly
        if( !m_bAllProtect && GetDoc()->GetDocShell() &&
            GetDoc()->GetDocShell()->IsReadOnlyUI() )
            return true;

        if( m_pCurrentCursor->HasMark() )
            ClearMark();

        // first check for frames
        SwNodeIndex& rNdIdx = m_pCurrentCursor->GetPoint()->nNode;
        sal_uLong nNdIdx = rNdIdx.GetIndex(); // keep backup
        SwNodes& rNds = mxDoc->GetNodes();
        SwContentNode* pCNd = rNdIdx.GetNode().GetContentNode();
        const SwContentFrame * pFrame;

        if( pCNd && nullptr != (pFrame = pCNd->getLayoutFrame( GetLayout(), nullptr, m_pCurrentCursor->GetPoint(), false)) &&
            !IsReadOnlyAvailable() && pFrame->IsProtected() &&
            nNdIdx < rNds.GetEndOfExtras().GetIndex() )
        {
            // skip protected frame
            SwPaM aPam( *m_pCurrentCursor->GetPoint() );
            aPam.SetMark();
            aPam.GetMark()->nNode = rNds.GetEndOfContent();
            aPam.GetPoint()->nNode = *pCNd->EndOfSectionNode();

            bool bFirst = false;
            if( nullptr == (pCNd = ::GetNode( aPam, bFirst, fnMoveForward )))
            {
                aPam.GetMark()->nNode = *rNds.GetEndOfPostIts().StartOfSectionNode();
                pCNd = ::GetNode( aPam, bFirst, fnMoveBackward );
            }

            if( !pCNd ) // should *never* happen
            {
                rNdIdx = nNdIdx; // back to old node
                return false;
            }
            *m_pCurrentCursor->GetPoint() = *aPam.GetPoint();
        }
        else if( bOnlyText && pCNd && pCNd->IsNoTextNode() )
        {
            // set to beginning of document
            rNdIdx = mxDoc->GetNodes().GetEndOfExtras();
            m_pCurrentCursor->GetPoint()->nContent.Assign( mxDoc->GetNodes().GoNext(
                                                                &rNdIdx ), 0 );
            nNdIdx = rNdIdx.GetIndex();
        }

        bool bOk = true;

        // #i9059# cursor may not stand in protected cells
        //         (unless cursor in protected areas is OK.)
        const SwTableNode* pTableNode = rNdIdx.GetNode().FindTableNode();
        if( !IsReadOnlyAvailable()  &&
            pTableNode != nullptr  &&  rNdIdx.GetNode().IsProtect() )
        {
            // we're in a table, and we're in a protected area, so we're
            // probably in a protected cell.

            // move forward into non-protected area.
            SwPaM aPam( rNdIdx.GetNode(), 0 );
            while( aPam.GetNode().IsProtect() &&
                   aPam.Move( fnMoveForward, GoInContent ) )
                ; // nothing to do in the loop; the aPam.Move does the moving!

            // didn't work? then go backwards!
            if( aPam.GetNode().IsProtect() )
            {
                SwPaM aTmpPaM( rNdIdx.GetNode(), 0 );
                aPam = aTmpPaM;
                while( aPam.GetNode().IsProtect() &&
                       aPam.Move( fnMoveBackward, GoInContent ) )
                    ; // nothing to do in the loop; the aPam.Move does the moving!
            }

            // if we're successful, set the new position
            if( ! aPam.GetNode().IsProtect() )
            {
                *m_pCurrentCursor->GetPoint() = *aPam.GetPoint();
            }
        }

        // in a protected frame
        const SwSectionNode* pSectNd = rNdIdx.GetNode().FindSectionNode();
        if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() ||
            ( !IsReadOnlyAvailable() &&
               pSectNd->GetSection().IsProtectFlag() )) )
        {
            bOk = false;
            bool bGoNextSection = true;
            for( int nLoopCnt = 0; !bOk && nLoopCnt < 2; ++nLoopCnt )
            {
                bool bContinue;
                do {
                    bContinue = false;
                    for (;;)
                    {
                        if (bGoNextSection)
                            pCNd = rNds.GoNextSection( &rNdIdx,
                                                true, !IsReadOnlyAvailable() );
                        else
                            pCNd = SwNodes::GoPrevSection( &rNdIdx,
                                                true, !IsReadOnlyAvailable() );
                        if ( pCNd == nullptr) break;
                        // moved inside a table -> check if it is protected
                        if( pCNd->FindTableNode() )
                        {
                            SwCallLink aTmp( *this );
                            SwCursorSaveState aSaveState( *m_pCurrentCursor );
                            aTmp.nNdTyp = SwNodeType::NONE; // don't do anything in DTOR
                            if( !m_pCurrentCursor->IsInProtectTable( true ) )
                            {
                                const SwSectionNode* pSNd = pCNd->FindSectionNode();
                                if( !pSNd || !pSNd->GetSection().IsHiddenFlag()
                                    || (!IsReadOnlyAvailable()  &&
                                        pSNd->GetSection().IsProtectFlag() ))
                                {
                                    bOk = true;
                                    break; // found non-protected cell
                                }
                                continue; // continue search
                            }
                        }
                        else
                        {
                            bOk = true;
                            break; // found non-protected cell
                        }
                    }

                    if( bOk && rNdIdx.GetIndex() < rNds.GetEndOfExtras().GetIndex() )
                    {
                        // also check for Fly - might be protected as well
                        if( nullptr == (pFrame = pCNd->getLayoutFrame( GetLayout(), nullptr, nullptr, false)) ||
                            ( !IsReadOnlyAvailable() && pFrame->IsProtected() ) ||
                            ( bOnlyText && pCNd->IsNoTextNode() ) )
                        {
                            // continue search
                            bOk = false;
                            bContinue = true;
                        }
                    }
                } while( bContinue );

                if( !bOk )
                {
                    if( !nLoopCnt )
                        bGoNextSection = false;
                    rNdIdx = nNdIdx;
                }
            }
        }
        if( bOk )
        {
            pCNd = rNdIdx.GetNode().GetContentNode();
            const sal_Int32 nContent = rNdIdx.GetIndex() < nNdIdx ? pCNd->Len() : 0;
            m_pCurrentCursor->GetPoint()->nContent.Assign( pCNd, nContent );
        }
        else
        {
            pCNd = rNdIdx.GetNode().GetContentNode();
            // if cursor in hidden frame, always move it
            if( !pCNd || !pCNd->getLayoutFrame( GetLayout(), nullptr, nullptr, false) )
            {
                SwCursorMoveState aTmpState( MV_NONE );
                aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
                GetLayout()->GetCursorOfst( m_pCurrentCursor->GetPoint(), m_pCurrentCursor->GetPtPos(),
                                            &aTmpState );
            }
        }
        return bOk;
    }

    bool SwCursorShell::IsCursorReadonly() const
    {
        if ( GetViewOptions()->IsReadonly() ||
             GetViewOptions()->IsFormView() /* Formula view */ )
        {
            SwFrame *pFrame = GetCurrFrame( false );
            const SwFlyFrame* pFly;
            const SwSection* pSection;

            if( pFrame && pFrame->IsInFly() &&
                (pFly = pFrame->FindFlyFrame())->GetFormat()->GetEditInReadonly().GetValue() &&
                pFly->Lower() &&
                !pFly->Lower()->IsNoTextFrame() &&
                !GetDrawView()->GetMarkedObjectList().GetMarkCount() )
            {
                return false;
            }
            // edit in readonly sections
            else if ( pFrame && pFrame->IsInSct() &&
                nullptr != ( pSection = pFrame->FindSctFrame()->GetSection() ) &&
                pSection->IsEditInReadonlyFlag() )
            {
                return false;
            }
            else if ( !IsMultiSelection() && CursorInsideInputField() )
            {
                return false;
            }

            return true;
        }
        return false;
    }

    /// is the cursor allowed to enter ReadOnly sections?
    void SwCursorShell::SetReadOnlyAvailable( bool bFlag )
    {
        // *never* switch in GlobalDoc
        if( (!GetDoc()->GetDocShell() ||
             dynamic_cast<const SwGlobalDocShell*>(GetDoc()->GetDocShell()) == nullptr ) &&
            bFlag != m_bSetCursorInReadOnly )
        {
            // If the flag is switched off then all selections need to be
            // invalidated. Otherwise we would trust that nothing protected is selected.
            if( !bFlag )
            {
                ClearMark();
            }
            m_bSetCursorInReadOnly = bFlag;
            UpdateCursor();
        }
    }

    bool SwCursorShell::HasReadonlySel() const
    {
        bool bRet = false;
        // If protected area is to be ignored, then selections are never read-only.
        if ((IsReadOnlyAvailable() || GetViewOptions()->IsFormView() ||
            GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM )) &&
            !SwViewOption::IsIgnoreProtectedArea())
        {
            if ( m_pTableCursor != nullptr )
            {
                bRet = m_pTableCursor->HasReadOnlyBoxSel()
                       || m_pTableCursor->HasReadonlySel( GetViewOptions()->IsFormView() );
            }
            else
            {
                for(const SwPaM& rCursor : m_pCurrentCursor->GetRingContainer())
                {
                    if( rCursor.HasReadonlySel( GetViewOptions()->IsFormView() ) )
                    {
                        bRet = true;
                        break;
                    }
                }
            }
        }
        return bRet;
    }

    bool SwCursorShell::IsSelFullPara() const
    {
        bool bRet = false;

        if( m_pCurrentCursor->GetPoint()->nNode.GetIndex() ==
            m_pCurrentCursor->GetMark()->nNode.GetIndex() && !m_pCurrentCursor->IsMultiSelection() )
        {
            sal_Int32 nStt = m_pCurrentCursor->GetPoint()->nContent.GetIndex();
            sal_Int32 nEnd = m_pCurrentCursor->GetMark()->nContent.GetIndex();
            if( nStt > nEnd )
            {
                sal_Int32 nTmp = nStt;
                nStt = nEnd;
                nEnd = nTmp;
            }
            const SwContentNode* pCNd = m_pCurrentCursor->GetContentNode();
            bRet = pCNd && !nStt && nEnd == pCNd->Len();
        }
        return bRet;
    }

    SvxFrameDirection SwCursorShell::GetTextDirection( const Point* pPt ) const
    {
        SwPosition aPos( *m_pCurrentCursor->GetPoint() );
        Point aPt( pPt ? *pPt : m_pCurrentCursor->GetPtPos() );
        if( pPt )
        {
            SwCursorMoveState aTmpState( MV_NONE );
            aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();

            GetLayout()->GetCursorOfst( &aPos, aPt, &aTmpState );
        }

        return mxDoc->GetTextDirection( aPos, &aPt );
    }

    bool SwCursorShell::IsInVerticalText( const Point* pPt ) const
    {
        const SvxFrameDirection nDir = GetTextDirection( pPt );
        return SvxFrameDirection::Vertical_RL_TB == nDir || SvxFrameDirection::Vertical_LR_TB == nDir;
    }

    bool SwCursorShell::IsInRightToLeftText() const
    {
        const SvxFrameDirection nDir = GetTextDirection();
        // GetTextDirection uses SvxFrameDirection::Vertical_LR_TB to indicate RTL in
        // vertical environment
        return SvxFrameDirection::Vertical_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir;
    }

    /// If the current cursor position is inside a hidden range, the hidden range
    /// is selected.
    bool SwCursorShell::SelectHiddenRange()
    {
        bool bRet = false;
        if ( !GetViewOptions()->IsShowHiddenChar() && !m_pCurrentCursor->HasMark() )
        {
            SwPosition& rPt = *m_pCurrentCursor->GetPoint();
            const SwTextNode* pNode = rPt.nNode.GetNode().GetTextNode();
            if ( pNode )
            {
                const sal_Int32 nPos = rPt.nContent.GetIndex();

                // check if nPos is in hidden range
                sal_Int32 nHiddenStart;
                sal_Int32 nHiddenEnd;
                SwScriptInfo::GetBoundsOfHiddenRange( *pNode, nPos, nHiddenStart, nHiddenEnd );
                if ( COMPLETE_STRING != nHiddenStart )
                {
                    // make selection:
                    m_pCurrentCursor->SetMark();
                    m_pCurrentCursor->GetMark()->nContent = nHiddenEnd;
                    bRet = true;
                }
            }
        }

        return bRet;
    }

    sal_uLong SwCursorShell::Find( const i18nutil::SearchOptions2& rSearchOpt,
                                 bool bSearchInNotes,
                                 SwDocPositions eStart, SwDocPositions eEnd,
                                 bool& bCancel,
                                 FindRanges eRng,
                                 bool bReplace )
    {
        if( m_pTableCursor )
            GetCursor();
        delete m_pTableCursor;
        m_pTableCursor = nullptr;
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        sal_uLong nRet = m_pCurrentCursor->Find( rSearchOpt, bSearchInNotes, eStart, eEnd,
                                         bCancel, eRng, bReplace );
        if( nRet || bCancel )
            UpdateCursor();
        return nRet;
    }

    sal_uLong SwCursorShell::Find( const SwTextFormatColl& rFormatColl,
                                 SwDocPositions eStart, SwDocPositions eEnd,
                                 bool& bCancel,
                                 FindRanges eRng,
                                 const SwTextFormatColl* pReplFormat )
    {
        if( m_pTableCursor )
            GetCursor();
        delete m_pTableCursor;
        m_pTableCursor = nullptr;
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        sal_uLong nRet = m_pCurrentCursor->Find( rFormatColl, eStart, eEnd, bCancel, eRng,
                                         pReplFormat );
        if( nRet )
            UpdateCursor();
        return nRet;
    }

    sal_uLong SwCursorShell::Find( const SfxItemSet& rSet,
                                 bool bNoCollections,
                                 SwDocPositions eStart, SwDocPositions eEnd,
                                 bool& bCancel,
                                 FindRanges eRng,
                                 const i18nutil::SearchOptions2* pSearchOpt,
                                 const SfxItemSet* rReplSet )
    {
        if( m_pTableCursor )
            GetCursor();
        delete m_pTableCursor;
        m_pTableCursor = nullptr;
        SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
        sal_uLong nRet = m_pCurrentCursor->Find( rSet, bNoCollections, eStart, eEnd,
                                         bCancel, eRng, pSearchOpt, rReplSet );
        if( nRet )
            UpdateCursor();
        return nRet;
    }

    void SwCursorShell::SetSelection( const SwPaM& rCursor )
    {
        StartAction();
        SwPaM* pCursor = GetCursor();
        *pCursor->GetPoint() = *rCursor.GetPoint();
        if(rCursor.HasMark())
        {
            pCursor->SetMark();
            *pCursor->GetMark() = *rCursor.GetMark();
        }
        if(rCursor.GetNext() != &rCursor)
        {
            const SwPaM *_pStartCursor = rCursor.GetNext();
            do
            {
                SwPaM* pCurrentCursor = CreateCursor();
                *pCurrentCursor->GetPoint() = *_pStartCursor->GetPoint();
                if(_pStartCursor->HasMark())
                {
                    pCurrentCursor->SetMark();
                    *pCurrentCursor->GetMark() = *_pStartCursor->GetMark();
                }
            } while( (_pStartCursor = _pStartCursor->GetNext()) != &rCursor );
        }
        EndAction();
    }

    static const SwStartNode* lcl_NodeContext( const SwNode& rNode )
    {
        const SwStartNode *pRet = rNode.StartOfSectionNode();
        while( pRet->IsSectionNode() || pRet->IsTableNode() ||
            pRet->GetStartNodeType() == SwTableBoxStartNode )
        {
            pRet = pRet->StartOfSectionNode();
        }
        return pRet;
    }

    /**
       Checks if a position is valid. To be valid the position's node must
       be a content node and the content must not be unregistered.

       @param aPos the position to check.
    */
    bool sw_PosOk(const SwPosition & aPos)
    {
        return nullptr != aPos.nNode.GetNode().GetContentNode() &&
               aPos.nContent.GetIdxReg();
    }

    /**
       Checks if a PaM is valid. For a PaM to be valid its point must be
       valid. Additionally if the PaM has a mark this has to be valid, too.

       @param aPam the PaM to check
    */
    static bool lcl_CursorOk(SwPaM & aPam)
    {
        return sw_PosOk(*aPam.GetPoint()) && (! aPam.HasMark()
            || sw_PosOk(*aPam.GetMark()));
    }

    void SwCursorShell::ClearUpCursors()
    {
        // start of the ring
        SwPaM * pStartCursor = GetCursor();
        // start loop with second entry of the ring
        SwPaM * pCursor = pStartCursor->GetNext();
        SwPaM * pTmpCursor;
        bool bChanged = false;

        // For all entries in the ring except the start entry delete the entry if
        // it is invalid.
        while (pCursor != pStartCursor)
        {
            pTmpCursor = pCursor->GetNext();
            if ( ! lcl_CursorOk(*pCursor))
            {
                delete pCursor;
                bChanged = true;
            }
            pCursor = pTmpCursor;
        }

        if( pStartCursor->HasMark() && !sw_PosOk( *pStartCursor->GetMark() ) )
        {
            pStartCursor->DeleteMark();
            bChanged = true;
        }
        if( !sw_PosOk( *pStartCursor->GetPoint() ) )
        {
            SwNodes & aNodes = GetDoc()->GetNodes();
            const SwNode* pStart = lcl_NodeContext( pStartCursor->GetPoint()->nNode.GetNode() );
            SwNodeIndex aIdx( pStartCursor->GetPoint()->nNode );
            SwNode * pNode = SwNodes::GoPrevious(&aIdx);
            if( pNode == nullptr || lcl_NodeContext( *pNode ) != pStart )
                aNodes.GoNext( &aIdx );
            if( pNode == nullptr || lcl_NodeContext( *pNode ) != pStart )
            {
                // If the start entry of the ring is invalid replace it with a
                // cursor pointing to the beginning of the first content node in the
                // document.
                aIdx = *(aNodes.GetEndOfContent().StartOfSectionNode());
                pNode = aNodes.GoNext( &aIdx );
            }
            bool bFound = (pNode != nullptr);

            assert(bFound);

            if (bFound)
            {
                SwPaM aTmpPam(*pNode);
                *pStartCursor = aTmpPam;
            }

            bChanged = true;
        }

        // If at least one of the cursors in the ring have been deleted or replaced,
        // remove the table cursor.
        if (m_pTableCursor != nullptr && bChanged)
            TableCursorToCursor();
    }

    OUString SwCursorShell::GetCursorDescr() const
    {
        OUString aResult;

        if (IsMultiSelection())
            aResult += SwResId(STR_MULTISEL);
        else
            aResult = SwDoc::GetPaMDescr(*GetCursor());

        return aResult;
    }

    void SwCursorShell::dumpAsXml(xmlTextWriterPtr pWriter) const
    {
        xmlTextWriterStartElement(pWriter, BAD_CAST("SwCursorShell"));

        SwViewShell::dumpAsXml(pWriter);

        xmlTextWriterStartElement(pWriter, BAD_CAST("m_pCurrentCursor"));
        for (SwPaM& rPaM : m_pCurrentCursor->GetRingContainer())
            rPaM.dumpAsXml(pWriter);
        xmlTextWriterEndElement(pWriter);

        xmlTextWriterEndElement(pWriter);
    }

    static void lcl_FillRecognizerData( std::vector< OUString >& rSmartTagTypes,
                                 uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps,
                                 const SwWrongList& rSmartTagList, sal_Int32 nCurrent )
    {
        // Insert smart tag information
        std::vector< uno::Reference< container::XStringKeyMap > > aStringKeyMaps;

        for ( sal_uInt16 i = 0; i < rSmartTagList.Count(); ++i )
        {
            const sal_Int32 nSTPos = rSmartTagList.Pos( i );
            const sal_Int32 nSTLen = rSmartTagList.Len( i );

            if ( nSTPos <= nCurrent && nCurrent < nSTPos + nSTLen )
            {
                const SwWrongArea* pArea = rSmartTagList.GetElement( i );
                if ( pArea )
                {
                    rSmartTagTypes.push_back( pArea->maType );
                    aStringKeyMaps.push_back( pArea->mxPropertyBag );
                }
            }
        }

        if ( !rSmartTagTypes.empty() )
        {
            rStringKeyMaps.realloc( rSmartTagTypes.size() );

            std::vector< uno::Reference< container::XStringKeyMap > >::const_iterator aMapsIter = aStringKeyMaps.begin();
            sal_uInt16 i = 0;
            for ( aMapsIter = aStringKeyMaps.begin(); aMapsIter != aStringKeyMaps.end(); ++aMapsIter )
                rStringKeyMaps[i++] = *aMapsIter;
        }
    }

    static void lcl_FillTextRange( uno::Reference<text::XTextRange>& rRange,
                       SwTextNode& rNode, sal_Int32 nBegin, sal_Int32 nLen )
    {
        // create SwPosition for nStartIndex
        SwIndex aIndex( &rNode, nBegin );
        SwPosition aStartPos( rNode, aIndex );

        // create SwPosition for nEndIndex
        SwPosition aEndPos( aStartPos );
        aEndPos.nContent = nBegin + nLen;

        const uno::Reference<text::XTextRange> xRange =
            SwXTextRange::CreateXTextRange(*rNode.GetDoc(), aStartPos, &aEndPos);

        rRange = xRange;
    }

    void SwCursorShell::GetSmartTagTerm( std::vector< OUString >& rSmartTagTypes,
                                       uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps,
                                       uno::Reference< text::XTextRange>& rRange ) const
    {
        if ( !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
            return;

        SwPaM* pCursor = GetCursor();
        SwPosition aPos( *pCursor->GetPoint() );
        SwTextNode *pNode = aPos.nNode.GetNode().GetTextNode();
        if ( pNode && !pNode->IsInProtectSect() )
        {
            const SwWrongList *pSmartTagList = pNode->GetSmartTags();
            if ( pSmartTagList )
            {
                sal_Int32 nCurrent = aPos.nContent.GetIndex();
                sal_Int32 nBegin = nCurrent;
                sal_Int32 nLen = 1;

                if (pSmartTagList->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin))
                {
                    const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin );
                    const SwWrongList* pSubList = pSmartTagList->SubList( nIndex );
                    if ( pSubList )
                    {
                        pSmartTagList = pSubList;
                        nCurrent = 0;
                    }

                    lcl_FillRecognizerData( rSmartTagTypes, rStringKeyMaps, *pSmartTagList, nCurrent );
                    lcl_FillTextRange( rRange, *pNode, nBegin, nLen );
                }
            }
        }
    }

    // see also SwEditShell::GetCorrection( const Point* pPt, SwRect& rSelectRect )
    void SwCursorShell::GetSmartTagRect( const Point& rPt, SwRect& rSelectRect )
    {
        SwPaM* pCursor = GetCursor();
        SwPosition aPos( *pCursor->GetPoint() );
        Point aPt( rPt );
        SwCursorMoveState eTmpState( MV_SETONLYTEXT );
        SwSpecialPos aSpecialPos;
        eTmpState.m_pSpecialPos = &aSpecialPos;
        SwTextNode *pNode;
        const SwWrongList *pSmartTagList;

        if( GetLayout()->GetCursorOfst( &aPos, aPt, &eTmpState ) &&
            nullptr != (pNode = aPos.nNode.GetNode().GetTextNode()) &&
            nullptr != (pSmartTagList = pNode->GetSmartTags()) &&
            !pNode->IsInProtectSect() )
        {
            sal_Int32 nBegin = aPos.nContent.GetIndex();
            sal_Int32 nLen = 1;

            if (pSmartTagList->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin))
            {
                // get smarttag word
                OUString aText( pNode->GetText().copy(nBegin, nLen) );

                //save the start and end positions of the line and the starting point
                Push();
                LeftMargin();
                const sal_Int32 nLineStart = GetCursor()->GetPoint()->nContent.GetIndex();
                RightMargin();
                const sal_Int32 nLineEnd = GetCursor()->GetPoint()->nContent.GetIndex();
                Pop(PopMode::DeleteCurrent);

                // make sure the selection build later from the data below does not
                // include "in word" character to the left and right in order to
                // preserve those. Therefore count those "in words" in order to
                // modify the selection accordingly.
                const sal_Unicode* pChar = aText.getStr();
                sal_Int32 nLeft = 0;
                while (*pChar++ == CH_TXTATR_INWORD)
                    ++nLeft;
                pChar = aText.getLength() ? aText.getStr() + aText.getLength() - 1 : nullptr;
                sal_Int32 nRight = 0;
                while (pChar && *pChar-- == CH_TXTATR_INWORD)
                    ++nRight;

                aPos.nContent = nBegin + nLeft;
                pCursor = GetCursor();
                *pCursor->GetPoint() = aPos;
                pCursor->SetMark();
                ExtendSelection( true, nLen - nLeft - nRight );
                // do not determine the rectangle in the current line
                const sal_Int32 nWordStart = (nBegin + nLeft) < nLineStart ? nLineStart : nBegin + nLeft;
                // take one less than the line end - otherwise the next line would
                // be calculated
                const sal_Int32 nWordEnd = std::min(nBegin + nLen - nLeft - nRight, nLineEnd);
                Push();
                pCursor->DeleteMark();
                SwIndex& rContent = GetCursor()->GetPoint()->nContent;
                rContent = nWordStart;
                SwRect aStartRect;
                SwCursorMoveState aState;
                aState.m_bRealWidth = true;
                SwContentNode* pContentNode = pCursor->GetContentNode();
                SwContentFrame *pContentFrame = pContentNode->getLayoutFrame( GetLayout(), &rPt, pCursor->GetPoint(), false);

                pContentFrame->GetCharRect( aStartRect, *pCursor->GetPoint(), &aState );
                rContent = nWordEnd - 1;
                SwRect aEndRect;
                pContentFrame->GetCharRect( aEndRect, *pCursor->GetPoint(),&aState );
                rSelectRect = aStartRect.Union( aEndRect );
                Pop(PopMode::DeleteCurrent);
            }
        }
    }

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

↑ V773 Visibility scope of the 'pCurrentCursor' pointer was exited without releasing the memory. A memory leak is possible.

↑ V773 Visibility scope of the 'pNew' pointer was exited without releasing the memory. A memory leak is possible.

↑ V773 Visibility scope of the 'pNew' pointer was exited without releasing the memory. A memory leak is possible.

↑ V773 The return value of function 'CreateCursor' is required to be utilized. A memory leak is possible.

↑ V560 A part of conditional expression is always true: pTableFrame.

↑ V560 A part of conditional expression is always true: pFrame.