/* -*- 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.