Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/source/core/crsr/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 149 kB image not shown  

Quelle  crsrsh.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <config_wasm_strip.h>

#include <com/sun/star/text/XTextRange.hpp>

#include <hintids.hxx>
#include <svx/srchdlg.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 <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 <GrammarContact.hxx>
#include <OnlineAccessibilityCheck.hxx>
#include <comphelper/flagguard.hxx>
#include <strings.hrc>
#include <IDocumentLayoutAccess.hxx>
#if ENABLE_YRS
#include <IDocumentState.hxx>
#endif
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
#include <comphelper/sequence.hxx>
#include <sfx2/lokhelper.hxx>
#include <editeng/editview.hxx>
#include <editeng/frmdir.hxx>
#include <sal/log.hxx>
#include <PostItMgr.hxx>
#include <DocumentSettingManager.hxx>
#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>
#include <tabcol.hxx>
#include <wrtsh.hxx>
#include <undobj.hxx>
#include <view.hxx>
#include <hints.hxx>
#include <tools/json_writer.hxx>
#include <redline.hxx>

using namespace com::sun::star;

/**
 * Check if pCurrentCursor points into already existing ranges and delete those.
 * @param Pointer to SwCursor object
 */

static void CheckRange( SwCursor* pCurrentCursor )
{
    auto [pStart, pEnd] = pCurrentCursor->StartEnd(); // SwPosition*

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

    // Search the complete ring
    while( pTmp != pCurrentCursor )
    {
        auto [pTmpStt, pTmpEnd] = pTmp->StartEnd(); // SwPosition*
        if( *pStart <= *pTmpStt )
        {
            if( *pEnd > *pTmpStt ||
                ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd ))
                pTmpDel = pTmp;
        }
        else
            if( *pStart < *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

/**
 * Add a copy of current cursor, append it after current, and collapse current cursor.
 * @return - Returns a newly created copy of current cursor.
 */

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

    // ensure that m_pCurrentCursor is valid; if it's invalid it would be
    // copied to pNew and then pNew would be deleted in UpdateCursor() below
    ClearUpCursors();

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

SwCursor & SwCursorShell::CreateNewShellCursor()
{
    if (HasSelection())
    {
        (void) CreateCursor(); // n.b. returns old cursor
    }
    return *GetCursor();
}

/**
 * Return the current shell cursor
 * @return - returns current `SwPaM` shell cursor
 */

SwCursor & SwCursorShell::GetCurrentShellCursor()
{
    return *GetCursor();
}

/**
 * Return pointer to the current shell cursor
 * @return - returns pointer to current `SwCursor` shell cursor
 */

SwCursor* SwCursorShell::GetCursor( bool bMakeTableCursor ) const
{
    if( m_pTableCursor )
    {
        if( bMakeTableCursor && m_pTableCursor->IsCursorMovedUpdate() )
        {
            //don't re-create 'parked' cursors
            if( m_pTableCursor->GetPoint()->GetNodeIndex() &&
                m_pTableCursor->GetMark()->GetNodeIndex() )
            {
                const SwContentNode* pCNd = m_pTableCursor->GetPointContentNode();
                if( pCNd && pCNd->getLayoutFrame( GetLayout() ) )
                {
                    pCNd = m_pTableCursor->GetMarkContentNode();
                    if( pCNd && 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()->GetNode();
        m_nCurrentNode = rNd.GetIndex();
        m_nCurrentContent = m_pCurrentCursor->GetPoint()->GetContentIndex();
        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 )
{
    comphelper::FlagRestorationGuard g(mbSelectAll, StartsWith_() != StartsWith::None && ExtendedSelectedAll());
    bool bVis = m_bSVCursorVis;

    // Idle-formatting?
    if( bIdleEnd && Imp()->HasPaintRegion() )
    {
        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;
    }

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

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

    {
        SwCallLink aLk( *this );           // Watch cursor moves,
        aLk.m_nNode = m_nCurrentNode;        // possibly call the link
        aLk.m_nNodeType = m_nCurrentNdTyp;
        aLk.m_nContent = m_nCurrentContent;
        aLk.m_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(nullptr);
        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 );
    --m_nCursorMove;
#ifdef DBG_UTIL
    if( !m_nCursorMove )
        m_bInCMvVisportChgd = false;
#endif
}

bool SwCursorShell::LeftRight( bool bLeft, sal_uInt16 nCnt, SwCursorSkipMode 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
        && pShellCursor->GetPoint()->GetNode().IsTextNode()
        && static_cast<SwTextFrame const*>(
            pShellCursor->GetPoint()->GetNode().GetTextNode()->getLayoutFrame(GetLayout())
            )->MapModelToViewPos(*pShellCursor->GetPoint()) == TextFrameIndex(0)
        && !pShellCursor->IsInFrontOfLabel()
        && !pShellCursor->HasMark()
        && nullptr != (pTextNd = sw::GetParaPropsNode(*GetLayout(), pShellCursor->GetPoint()->GetNode()))
        && 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(),
                                      GetLayout(),
                                      GetViewOptions()->IsFieldName());
        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)
        return;

    // Writer redraws the "marked" list with the field shading, if there
    // is no field shading then the marked list would be redrawn for no
    // visually identifiable reason, so skip the mark if field shadings
    // are disabled.
    const bool bVisuallyMarked(GetViewOptions()->IsFieldShadings());
    if (bVisuallyMarked)
    {
        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 const*const pTextNd = sw::GetParaPropsNode(*GetLayout(),
            GetCursor_()->GetPoint()->GetNode());

    if ( !pTextNd )
        return;

    if (!pTextNd->IsNumbered(GetLayout()))
    {
        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 = pParent;
        }
        else
        {
            break;
        }
    }

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

bool SwCursorShell::UpDown( bool bUp, sal_uInt16 nCnt )
{
    CurrShell aCurr( 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 = CursorMoveState::UpDown; // status for Cursor travelling - GetModelPositionForViewPoint
        if( !ActionPend() )
        {
            CursorFlag eUpdateMode = SwCursorShell::SCROLLWIN;
            if( !bTableMode )
                eUpdateMode = static_cast<CursorFlag>(eUpdateMode
                            | SwCursorShell::UPDOWN | SwCursorShell::CHKRANGE);
            UpdateCursor( o3tl::narrowing<sal_uInt16>(eUpdateMode) );
        }
    }
    return bRet;
}

bool SwCursorShell::LRMargin( bool bLeft, bool bAPI)
{
    SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
    CurrShell aCurr( this );
    m_eMvState = CursorMoveState::LeftMargin; // status for Cursor travelling - GetModelPositionForViewPoint

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

    if( m_pBlockCursor )
        m_pBlockCursor->clearPoints();

    const bool bWasAtLM = GetCursor_()->IsAtLeftRightMargin(*GetLayout(), true, bAPI);

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

    if ( bLeft && !bTableMode && bRet && bWasAtLM && !GetCursor_()->HasMark() )
    {
        const SwTextNode * pTextNd = GetCursor_()->GetPointNode().GetTextNode();
        assert(sw::GetParaPropsNode(*GetLayout(), GetCursor_()->GetPoint()->GetNode()) == pTextNd);
        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(*GetLayout(), 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;
}

const SwTableNode* SwCursorShell::IsCursorInTable() const
{
    if (m_pTableCursor && m_pTableCursor->GetSelectedBoxesCount())
    {   // find the table that has the selected boxes
        return m_pTableCursor->GetSelectedBoxes()[0]->GetSttNd()->FindTableNode();
    }
    return m_pCurrentCursor->GetPointNode().FindTableNode();
}

// fun cases to consider:
// * outermost table
//   - into para => SA/ESA
//   - into prev/next table => continue...
//   - no prev/next => done
// * inner table
//   - into containing cell => SA/ESA
//   - into prev/next of containing cell
//      + into para
//      + into table nested in prev/next cell
//   - out of table -> as above
// => iterate in one direction until a node is reached that is a parent or a sibling of a parent of the current table
// - parent reached => SA/ESA depending
// - not in parent but in *prev/next* sibling of outer cell => TrySelectOuterTable
// - not in parent but in *prev/next* sibling of outer table => TrySelectOuterTable
//   => select-all cannot select a sequence of table with no para at same level; only 1 table
// - no parent, no prev/next => TrySelectOuterTable

bool SwCursorShell::MoveOutOfTable()
{
    SwPosition const point(*getShellCursor(false)->GetPoint());
    SwPosition const mark(*getShellCursor(false)->GetMark());

    for (auto const fnMove : {&fnMoveBackward, &fnMoveForward})
    {
        Push();
        SwCursor *const pCursor(getShellCursor(false));

        pCursor->Normalize(fnMove == &fnMoveBackward);
        pCursor->DeleteMark();
        SwTableNode const*const pTable(pCursor->GetPoint()->GetNode().FindTableNode());
        assert(pTable);
        while (MovePara(GoInContent, *fnMove))
        {
            SwStartNode const*const pBox(pCursor->GetPoint()->GetNode().FindTableBoxStartNode());
            if (!pBox)
            {
                Pop(SwCursorShell::PopMode::DeleteStack);
                return true// moved to paragraph at top-level of text
            }
            if (pBox->GetIndex() < pTable->GetIndex()
                && pTable->EndOfSectionIndex() < pBox->EndOfSectionIndex())
            {
                Pop(SwCursorShell::PopMode::DeleteStack);
                return true// pBox contains start position (pTable)
            }
        }

        Pop(SwCursorShell::PopMode::DeleteCurrent);
        // FIXME: Pop doesn't restore original cursor if nested tables
        *getShellCursor(false)->GetPoint() = point;
        getShellCursor(false)->SetMark();
        *getShellCursor(false)->GetMark() = mark;
    }
    return false;
}

bool SwCursorShell::TrySelectOuterTable()
{
    assert(m_pTableCursor);
    SwTableNode const& rInnerTable(*m_pTableCursor->GetPoint()->GetNode().FindTableNode());
    SwNodes const& rNodes(rInnerTable.GetNodes());
    SwTableNode const*const pOuterTable(rInnerTable.GetNodes()[rInnerTable.GetIndex()-1]->FindTableNode());
    if (!pOuterTable)
    {
        return false;
    }

    // manually select boxes of pOuterTable
    SwNodeIndex firstCell(*pOuterTable, +1);
    SwNodeIndex lastCell(*rNodes[pOuterTable->EndOfSectionIndex()-1]->StartOfSectionNode());
    SwSelBoxes aNew;
    pOuterTable->GetTable().CreateSelection(&firstCell.GetNode(), &lastCell.GetNode(),
            aNew, SwTable::SEARCH_NONE, false);
    // set table cursor to 1st / last content which may be in inner table
    SwContentNode* const pStart = SwNodes::GoNext(&firstCell);
    assert(pStart); // must at least find the previous point node
    lastCell = *lastCell.GetNode().EndOfSectionNode();
    SwContentNode *const pEnd = SwNodes::GoPrevious(&lastCell);
    assert(pEnd); // must at least find the previous point node
    delete m_pTableCursor;
    m_pTableCursor = new SwShellTableCursor(*this, SwPosition(*pStart, 0), Point(),
            SwPosition(*pEnd, 0), Point());
    m_pTableCursor->ActualizeSelection( aNew );
    m_pTableCursor->IsCursorMovedUpdate(); // clear this so GetCursor() doesn't recreate our SwSelBoxes

    // this will update m_pCurrentCursor based on m_pTableCursor
    UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);

    return true;
}

/// find XText start node
static SwStartNode const* FindTextStart(SwPosition const& rPos)
{
    SwStartNode const* pStartNode(rPos.GetNode().StartOfSectionNode());
    while (pStartNode && (pStartNode->IsSectionNode() || pStartNode->IsTableNode()))
    {
        pStartNode = pStartNode->StartOfSectionNode();
    }
    return pStartNode;
}

static SwStartNode const* FindParentText(SwShellCursor const& rCursor)
{
    // find closest section containing both start and end - ignore Sections
    SwStartNode const* pStartNode(FindTextStart(*rCursor.Start()));
    SwEndNode const* pEndNode(FindTextStart(*rCursor.End())->EndOfSectionNode());
    while (pStartNode->EndOfSectionNode()->GetIndex() < pEndNode->GetIndex())
    {
        pStartNode = pStartNode->StartOfSectionNode();
    }
    while (pStartNode->GetIndex() < pEndNode->StartOfSectionNode()->GetIndex())
    {
        pEndNode = pEndNode->StartOfSectionNode()->StartOfSectionNode()->EndOfSectionNode();
    }
    assert(pStartNode->EndOfSectionNode() == pEndNode);

    return (pStartNode->IsSectionNode() || pStartNode->IsTableNode())
        ? FindTextStart(SwPosition(*pStartNode))
        : pStartNode;
}

bool SwCursorShell::MoveStartText()
{
    SwPosition const old(*m_pCurrentCursor->GetPoint());
    SwStartNode const*const pStartNode(FindParentText(*getShellCursor(false)));
    assert(pStartNode);
    SwTableNode const*const pTable(pStartNode->FindTableNode());
    m_pCurrentCursor->GetPoint()->Assign(*pStartNode);
    SwNodes::GoNext(m_pCurrentCursor->GetPoint());
    while (auto* pFoundTable = m_pCurrentCursor->GetPoint()->GetNode().FindTableNode())
    {
        if (pFoundTable == pTable)
            break;
        if (pTable && pTable->GetIndex() >= pFoundTable->GetIndex())
            break;
        if (!MoveOutOfTable())
            break;
    }
    UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
    return old != *m_pCurrentCursor->GetPoint();
}

// select all inside the current XText, with table or hidden para at start/end
void SwCursorShell::ExtendedSelectAll(bool bFootnotes)
{
    // find common ancestor node of both ends of cursor
    SwStartNode const*const pStartNode(FindParentText(*getShellCursor(false)));
    assert(pStartNode);
    if (IsTableMode())
    {   // convert m_pTableCursor to m_pCurrentCursor after determining pStartNode
        TableCursorToCursor();
    }
    SwNodes& rNodes = GetDoc()->GetNodes();
    m_pCurrentCursor->Normalize(true);
    SwPosition* pPos = m_pCurrentCursor->GetPoint();
    pPos->Assign(bFootnotes ? rNodes.GetEndOfPostIts() : static_cast<SwNode const&>(*pStartNode));
    SwNodes::GoNext(pPos);
    pPos = m_pCurrentCursor->GetMark();
    pPos->Assign(bFootnotes ? rNodes.GetEndOfContent() : static_cast<SwNode const&>(*pStartNode->EndOfSectionNode()));
    SwContentNode* pCNd = SwNodes::GoPrevious( pPos );
    if (pCNd)
        pPos->AssignEndIndex(*pCNd);
}

static typename SwCursorShell::StartsWith StartsWith(SwStartNode const& rStart)
{
    for (auto i = rStart.GetIndex() + 1; i < rStart.EndOfSectionIndex(); ++i)
    {
        SwNode const& rNode(*rStart.GetNodes()[i]);
        switch (rNode.GetNodeType())
        {
            case SwNodeType::Section:
                if (rNode.GetSectionNode()->GetSection().IsHidden())
                    return SwCursorShell::StartsWith::HiddenSection;
                continue;
            case SwNodeType::Table:
                return SwCursorShell::StartsWith::Table;
            case SwNodeType::Text:
                if (rNode.GetTextNode()->IsHidden())
                {
                    return SwCursorShell::StartsWith::HiddenPara;
                }
                return SwCursorShell::StartsWith::None;
            default:
                return SwCursorShell::StartsWith::None;
        }
    }
    return SwCursorShell::StartsWith::None;
}

static typename SwCursorShell::StartsWith EndsWith(SwStartNode const& rStart)
{
    for (auto i = rStart.EndOfSectionIndex() - 1; rStart.GetIndex() < i; --i)
    {
        SwNode const& rNode(*rStart.GetNodes()[i]);
        switch (rNode.GetNodeType())
        {
            case SwNodeType::End:
                if (auto pStartNode = rNode.StartOfSectionNode(); pStartNode->IsTableNode())
                {
                    return SwCursorShell::StartsWith::Table;
                }
                else if (pStartNode->IsSectionNode())
                {
                    if (pStartNode->GetSectionNode()->GetSection().IsHidden())
                        return SwCursorShell::StartsWith::HiddenSection;
                }
                    //TODO buggy SwUndoRedline in testTdf137503?                assert(rNode.StartOfSectionNode()->IsSectionNode());
            break;
            case SwNodeType::Text:
                if (rNode.GetTextNode()->IsHidden())
                {
                    return SwCursorShell::StartsWith::HiddenPara;
                }
                return SwCursorShell::StartsWith::None;
            default:
                return SwCursorShell::StartsWith::None;
        }
    }
    return SwCursorShell::StartsWith::None;
}

// return the node that is the start of the extended selection (to include table
// or section start nodes; looks like extending for end nodes is not required)
SwCursorShell::ExtendedSelection SwCursorShell::ExtendedSelectedAll() const
{
    if (m_pTableCursor)
    {
        return {};
    }

    SwNodes& rNodes = GetDoc()->GetNodes();
    SwShellCursor const*const pShellCursor = getShellCursor(false);
    SwStartNode const* pStartNode(FindParentText(*pShellCursor));

    SwNodeIndex nNode(*pStartNode);
    SwContentNode* pStart = SwNodes::GoNext(&nNode);
    if (!pStart)
    {
        return {};
    }

    nNode = *pStartNode->EndOfSectionNode();
    SwContentNode* pEnd = SwNodes::GoPrevious(&nNode);
    if (!pEnd)
    {
        return {};
    }

    SwPosition aStart(*pStart, 0);
    SwPosition aEnd(*pEnd, pEnd->Len());
    if (!(aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End()))
    {
        return {};
    }

    auto const ends(::EndsWith(*pStartNode));
    if (::StartsWith(*pStartNode) == StartsWith::None
        && ends == StartsWith::None)
    {
        return {}; // "ordinary" selection will work
    }

    ::std::vector<SwTableNode*> tablesAtEnd;
    if (ends == StartsWith::Table)
    {
        SwNode * pLastNode(rNodes[pStartNode->EndOfSectionIndex() - 1]);
        while (pLastNode->IsEndNode())
        {
            SwNode *const pNode(pLastNode->StartOfSectionNode());
            if (pNode->IsTableNode())
            {
                tablesAtEnd.push_back(pNode->GetTableNode());
                pLastNode = rNodes[pNode->GetIndex() - 1];
            }
            else if (pNode->IsSectionNode())
            {
                pLastNode = rNodes[pLastNode->GetIndex() - 1];
            }
        }
        assert(!tablesAtEnd.empty());
    }

    // tdf#133990 ensure directly containing section is included in SwUndoDelete
    while (pStartNode->IsSectionNode()
        && pStartNode->GetIndex() == pStartNode->StartOfSectionNode()->GetIndex() + 1
        && pStartNode->EndOfSectionNode()->GetIndex() + 1 == pStartNode->StartOfSectionNode()->EndOfSectionNode()->GetIndex())
    {
        pStartNode = pStartNode->StartOfSectionNode();
    }

    // pStartNode is the node that fully contains the selection - the first
    // node of the selection is the first node inside pStartNode
    return ::std::make_pair(rNodes[pStartNode->GetIndex() + 1], tablesAtEnd);
}

typename SwCursorShell::StartsWith SwCursorShell::StartsWith_()
{
    SwShellCursor const*const pShellCursor = getShellCursor(false);
    // first, check if this is invalid; ExtendedSelectAll(true) may result in
    // a) an ordinary selection that is valid
    // b) a selection that is extended
    // c) a selection that is invalid and will cause FindParentText to loop
    SwNode const& rEndOfExtras(GetDoc()->GetNodes().GetEndOfExtras());
    if (pShellCursor->Start()->nNode.GetIndex() <= rEndOfExtras.GetIndex()
        && rEndOfExtras.GetIndex() < pShellCursor->End()->nNode.GetIndex())
    {
        return StartsWith::None; // *very* extended, no ExtendedSelectedAll handling!
    }
    SwStartNode const*const pStartNode(FindParentText(*pShellCursor));
    if (auto const ret = ::StartsWith(*pStartNode); ret != StartsWith::None)
    {
        return ret;
    }
    if (auto const ret = ::EndsWith(*pStartNode); ret != StartsWith::None)
    {
        return ret;
    }
    return StartsWith::None;
}

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
        CurrShell aCurr( this );

        SwCursorSaveState aSaveState( *m_pCurrentCursor );
        Point& rPt = m_pCurrentCursor->GetPtPos();
        std::pair<Point, bool> tmp(rPt, false);
        SwContentFrame * pFrame = m_pCurrentCursor->GetPointContentNode()->
            getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp);
        if( pFrame && GetFrameInPage( pFrame, fnWhichPage, fnPosPage, m_pCurrentCursor ) &&
            !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
                                 SwCursorSelOverFlags::ChangePos ))
        {
            UpdateCursor();
            bRet = true;
        }
    }
    return bRet;
}

bool SwCursorShell::isInHiddenFrame(SwShellCursor* pShellCursor)
{
    SwContentNode *pCNode = pShellCursor->GetPointContentNode();
    std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false);
    SwContentFrame *const pFrame = pCNode
        ? pCNode->getLayoutFrame(GetLayout(), pShellCursor->GetPoint(), &tmp)
        : nullptr;
    return !pFrame || pFrame->IsHiddenNow();
}

// sw_redlinehide: this should work for all cases: GoCurrPara, GoNextPara, GoPrevPara
static bool IsAtStartOrEndOfFrame(SwCursorShell const*const pShell,
    SwShellCursor const*const pShellCursor, SwMoveFnCollection const& fnPosPara)
{
    SwContentNode *const pCNode = pShellCursor->GetPointContentNode();
    assert(pCNode); // surely can't have moved otherwise?
    std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false);
    SwContentFrame const*const pFrame = pCNode->getLayoutFrame(
            pShell->GetLayout(), pShellCursor->GetPoint(), &tmp);
    if (!pFrame || !pFrame->IsTextFrame())
    {
        return false;
    }
    SwTextFrame const& rTextFrame(static_cast<SwTextFrame const&>(*pFrame));
    TextFrameIndex const ix(rTextFrame.MapModelToViewPos(*pShellCursor->GetPoint()));
    if (&fnParaStart == &fnPosPara)
    {
        return ix == TextFrameIndex(0);
    }
    else
    {
        assert(&fnParaEnd == &fnPosPara);
        return ix == TextFrameIndex(rTextFrame.GetText().getLength());
    }
}

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 (isInHiddenFrame(pTmpCursor)
                || !IsAtStartOrEndOfFrame(this, pTmpCursor, fnPosPara))
        {
            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( SwNode& rNd, Point& rPt )
{
    SwFrame* pFrame = nullptr;
    SwContentNode* pCNd = rNd.GetContentNode();
    if( pCNd )
    {
        std::pair<Point, bool> tmp(rPt, false);
        SwContentFrame *pContentFrame = pCNd->getLayoutFrame(
            pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
            nullptr, &tmp);
        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()->GetNode(), aPt );
    if( pFrame && pbInHeader )
        *pbInHeader = pFrame->IsHeaderFrame();
    return nullptr != pFrame;
}

int SwCursorShell::SetCursor(const Point& rLPt, bool bOnlyText, bool bBlock,
    bool bFieldInfo, ScrollSizeMode eScrollSizeMode)
{
    CurrShell aCurr( this );

    SwShellCursor* pCursor = getShellCursor( bBlock );
    SwPosition aPos( *pCursor->GetPoint() );
    Point aPt( rLPt );
    Point & rCurrentCursorPt = pCursor->GetPtPos();
    SwCursorMoveState aTmpState( IsTableMode() ? CursorMoveState::TableSel :
                                    bOnlyText ?  CursorMoveState::SetOnlyText : CursorMoveState::NONE );
    aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
    aTmpState.m_bFieldInfo = bFieldInfo; // always set cursor at field-start if point is over field
    aTmpState.m_bPosMatchesBounds = bFieldInfo; // always set cursor at character-start if over char

    SwTextNode const*const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->GetNode());

    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()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState )
                    ? 0 : CRSR_POSCHG );

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

    pCursor->SetCursorBidiLevel( aTmpState.m_nCursorBidiLevel );

    if( CursorMoveState::RightMargin == aTmpState.m_eState )
        m_eMvState = CursorMoveState::RightMargin;
    // is the new position in header or footer?
    SwFrame* pFrame = lcl_IsInHeaderFooter( aPos.GetNode(), aPt );
    if( IsTableMode() && !pFrame && aPos.GetNode().StartOfSectionNode() ==
        pCursor->GetPoint()->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().Contains( rCurrentCursorPt ))
                    return bRet;
            }
            else if( aPos.GetNode().IsContentNode() )
            {
                // in the same frame?
                std::pair<Point, bool> tmp(m_aCharRect.Pos(), false);
                SwFrame* pOld = static_cast<SwContentNode&>(aPos.GetNode()).getLayoutFrame(
                                GetLayout(), nullptr, &tmp);
                tmp.first = aPt;
                SwFrame* pNew = static_cast<SwContentNode&>(aPos.GetNode()).getLayoutFrame(
                                GetLayout(), nullptr, &tmp);
                if( pNew == pOld )
                    return bRet;
            }
        }
    }
    else
    {
        // SSelection over not allowed sections or if in header/footer -> different
        if( !CheckNodesRange( aPos.GetNode(), pCursor->GetMark()->GetNode(), true )
            || ( pFrame && !pFrame->getFrameArea().Contains( 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, false, eScrollSizeMode );
        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 = CursorMoveState::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 )
{
    CurrShell aCurr( this );

    // check if the SPoint is in a table selection
    if( m_pTableCursor )
        return m_pTableCursor->Contains( 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( CursorMoveState::NONE );
    aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
    aTmpState.m_bPosMatchesBounds = true// treat last half of character same as first half
    if ( !GetLayout()->GetModelPositionForViewPoint( &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 SwCursor *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::IsSelOnePara() const
{
    if (m_pCurrentCursor->IsMultiSelection())
    {
        return false;
    }
    if (m_pCurrentCursor->GetPoint()->GetNode() == m_pCurrentCursor->GetMark()->GetNode())
    {
        return true;
    }
    if (GetLayout()->HasMergedParas())
    {
        SwContentFrame const*const pFrame(GetCurrFrame(false));
        auto const n(m_pCurrentCursor->GetMark()->GetNodeIndex());
        return FrameContainsNode(*pFrame, n);
    }
    return false;
}

bool SwCursorShell::IsSelStartPara() const
{
    if (m_pCurrentCursor->IsMultiSelection())
    {
        return false;
    }
    if (m_pCurrentCursor->GetPoint()->GetContentIndex() == 0 ||
                    m_pCurrentCursor->GetMark()->GetContentIndex() == 0)
    {
        return true;
    }
    if (GetLayout()->HasMergedParas())
    {
        SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->GetNode().GetTextNode());
        if (pNode)
        {
            SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(
                        pNode->getLayoutFrame(GetLayout())));
            if (pFrame)
            {
                return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetPoint())
                    == TextFrameIndex(0);
            }
        }
        SwTextNode const*const pNode2(m_pCurrentCursor->GetMark()->GetNode().GetTextNode());
        if (pNode2)
        {
            SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(
                        pNode2->getLayoutFrame(GetLayout())));
            if (pFrame)
            {
                return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetMark())
                    == TextFrameIndex(0);
            }
        }
    }
    return false;
}

bool SwCursorShell::IsSttPara() const
{
    if (GetLayout()->HasMergedParas())
    {
        SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->GetNode().GetTextNode());
        if (pNode)
        {
            SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(
                        pNode->getLayoutFrame(GetLayout())));
            if (pFrame)
            {
                return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetPoint())
                    == TextFrameIndex(0);
            }
        }
    }
    return m_pCurrentCursor->GetPoint()->GetContentIndex() == 0;
}

bool SwCursorShell::IsEndPara() const
{
    if (GetLayout()->HasMergedParas())
    {
        SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->GetNode().GetTextNode());
        if (pNode)
        {
            SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(
                        pNode->getLayoutFrame(GetLayout())));
            if (pFrame)
            {
                return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetPoint())
                    == TextFrameIndex(pFrame->GetText().getLength());
            }
        }
    }
    return m_pCurrentCursor->GetPoint()->GetContentIndex() == m_pCurrentCursor->GetPointContentNode()->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()->GetNode());
}

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

Point SwCursorShell::GetCursorPagePos() const
{
    Point aRet(-1, -1);
    if (SwFrame *pFrame = GetCurrFrame())
    {
        if (SwPageFrame* pCurrentPage = pFrame->FindPageFrame())
        {
            const Point& rDocPos = GetCursorDocPos();
            aRet = rDocPos - pCurrentPage->getFrameArea().TopLeft();
        }
    }
    return aRet;
}

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 )
{
    CurrShell aCurr( 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 )
{
    CurrShell aCurr( 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()
{
    CurrShell aCurr(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 || 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 )
{
    CurrShell aCurr( 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()
{
    CurrShell aCurr( 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(OUString::number(pFrame->getFrameArea().Left())
            + ", "
            + OUString::number(pFrame->getFrameArea().Top())
            + ", "
            + OUString::number(pFrame->getFrameArea().Width())
            + ", "
            + OUString::number(pFrame->getFrameArea().Height())
            + "; ");
    }
    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;

    CurrShell aCurr( 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;

    CurrShell aCurr( 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::GoNextPrevCursorSetSearchLabel(const bool bNext)
{
    SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );

    if( !m_pCurrentCursor->IsMultiSelection() )
    {
        if( !m_pCurrentCursor->HasMark() )
            SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
        return;
    }

    if (bNext)
        GoNextCursor();
    else
        GoPrevCursor();
}

void SwCursorShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect)
{
    comphelper::FlagRestorationGuard g(mbSelectAll, StartsWith_() != StartsWith::None && ExtendedSelectedAll());
    CurrShell aCurr( 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.Overlaps( m_aCharRect ) )
    {
        bVis = true;
        m_pVisibleCursor->Hide();
    }
#if ENABLE_YRS
    for (auto const& it : m_PeerCursors)
    {
        if (it.second->m_pCurrentCursor)
        {
            it.second->m_pVisibleCursor->Hide();
        }
    }
#endif

    // 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();
#if ENABLE_YRS
    for (auto const& it : m_PeerCursors)
    {
        if (it.second->m_pCurrentCursor && it.second->m_bSVCursorVis)
        {
            it.second->m_pVisibleCursor->Show();
        }
    }
#endif
}

void SwCursorShell::VisPortChgd( const SwRect & rRect )
{
    CurrShell aCurr( 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 moved (e.g. when deleting a
    text frame). The new position is calculated from its current position
    in the layout.
*/

void SwCursorShell::UpdateCursorPos()
{
    CurrShell aCurr( this );
    ++mnStartAction;
    SwShellCursor* pShellCursor = getShellCursor( true );
    Size aOldSz( GetDocSize() );

    if (isInHiddenFrame(pShellCursor) && !ExtendedSelectedAll())
    {
        // kde45196-1.html: try to get to a non-hidden paragraph, there must
        // be one in the document body
        while (isInHiddenFrame(pShellCursor))
        {
            if (!pShellCursor->MovePara(GoNextPara, fnParaStart))
            {
                break;
            }
        }
        while (isInHiddenFrame(pShellCursor))
        {
            if (!pShellCursor->MovePara(GoPrevPara, fnParaStart))
            {
                break;
            }
        }
        if (isInHiddenFrame(pShellCursor))
        {
            SwCursorMoveState aTmpState(CursorMoveState::SetOnlyText);
            aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
            GetLayout()->GetModelPositionForViewPoint(pShellCursor->GetPoint(),
                                                      pShellCursor->GetPtPos(), &aTmpState);
            pShellCursor->DeleteMark();
        }
    }
    auto* pDoc = GetDoc();
    if (pDoc)
    {
        pDoc->getGrammarContact()->updateCursorPosition(*m_pCurrentCursor->GetPoint());
        pDoc->getOnlineAccessibilityCheck()->update(*m_pCurrentCursor->GetPoint());
    }

    --mnStartAction;
    if( aOldSz != GetDocSize() )
        SizeChgNotify();
}

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

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

    if ( pTextNd )
        rPos.Assign( *pTextNd, 0 );
}

#if !ENABLE_WASM_STRIP_ACCESSIBILITY
namespace {

// #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();
        }
};

}
#endif

void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd, ScrollSizeMode eScrollSizeMode )
{
    CurrShell aCurr( this );
    ClearUpCursors();

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

    if (m_bNeedLayoutOnCursorUpdate)
    {
        // A previous spell check skipped a word that had a spelling error, because that word
        // had cursor. Now schedule the idle to call SwViewShell::LayoutIdle, to repeat the
        // spell check, in the hope that the cursor has left the word.
        m_aLayoutIdle.Start();
        m_bNeedLayoutOnCursorUpdate = false;
    }

#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    SwNotifyAccAboutInvalidTextSelections aInvalidateTextSelections( *this );
#endif

    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 );
--> --------------------

--> maximum size reached

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

Messung V0.5
C=94 H=89 G=91

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.