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

Quelle  docnum.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 <hintids.hxx>
#include <ftninfo.hxx>
#include <ftnidx.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentListsAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentState.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <poolfmt.hxx>
#include <UndoCore.hxx>
#include <UndoRedline.hxx>
#include <UndoNumbering.hxx>
#include <swundo.hxx>
#include <SwUndoFmt.hxx>
#include <rolbck.hxx>
#include <paratr.hxx>
#include <docary.hxx>
#include <mvsave.hxx>
#include <txtfrm.hxx>
#include <rootfrm.hxx>
#include <redline.hxx>
#include <strings.hrc>
#include <SwNodeNum.hxx>
#include <list.hxx>
#include <calbck.hxx>
#include <editeng/lrspitem.hxx>
#include <comphelper/string.hxx>
#include <comphelper/random.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>
#include <tools/datetimeutils.hxx>
//#include <fmtanchr.hxx>

#include <map>
#include <stdlib.h>

#include <wrtsh.hxx>

namespace {
    void lcl_ResetIndentAttrs(SwDoc *pDoc, const SwPaM &rPam,
            const o3tl::sorted_vector<sal_uInt16> aResetAttrsArray,
            SwRootFrame const*const pLayout)
    {
        // #i114929#
        // On a selection setup a corresponding Point-and-Mark in order to get
        // the indentation attribute reset on all paragraphs touched by the selection
        if ( rPam.HasMark() &&
             rPam.End()->GetNode().GetTextNode() )
        {
            SwPaM aPam( rPam.Start()->GetNode(), 0,
                        rPam.End()->GetNode(), rPam.End()->GetNode().GetTextNode()->Len() );
            pDoc->ResetAttrs( aPam, false, aResetAttrsArray, true, pLayout );
        }
        else
        {
            pDoc->ResetAttrs( rPam, false, aResetAttrsArray, true, pLayout );
        }
    }

    void ExpandPamForParaPropsNodes(SwPaM& rPam, SwRootFrame const*const pLayout)
    {
        if (!pLayout)
            return;

        // ensure that selection from the Shell includes the para-props node
        // to which the attributes should be applied
        if (rPam.GetPoint()->GetNode().IsTextNode())
        {
            rPam.GetPoint()->Assign( *sw::GetParaPropsNode(*pLayout, rPam.GetPoint()->GetNode()) );
        }
        if (rPam.GetMark()->GetNode().IsTextNode())
        {
            rPam.GetMark()->Assign( *sw::GetParaPropsNode(*pLayout, rPam.GetMark()->GetNode()) );
        }
    }
}

static sal_uInt8 GetUpperLvlChg( sal_uInt8 nCurLvl, sal_uInt8 nLevel, sal_uInt16 nMask )
{
    if( 1 < nLevel )
    {
        if( nCurLvl + 1 >= nLevel )
            nCurLvl -= nLevel - 1;
        else
            nCurLvl = 0;
    }
    return static_cast<sal_uInt8>((nMask - 1) & ~(( 1 << nCurLvl ) - 1));
}

void SwDoc::SetOutlineNumRule( const SwNumRule& rRule )
{
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().StartUndo(SwUndoId::OUTLINE_EDIT, nullptr);
        if (mpOutlineRule)
        {
            GetIDocumentUndoRedo().AppendUndo(
                std::make_unique<SwUndoOutlineEdit>(*mpOutlineRule, rRule, *this));
        }
    }

    if( mpOutlineRule )
        (*mpOutlineRule) = rRule;
    else
    {
        mpOutlineRule = new SwNumRule( rRule );

        AddNumRule(mpOutlineRule); // #i36749#
    }

    mpOutlineRule->SetRuleType( OUTLINE_RULE );
    mpOutlineRule->SetName(SwNumRule::GetOutlineRuleName(), getIDocumentListsAccess());

    // assure that the outline numbering rule is an automatic rule
    mpOutlineRule->SetAutoRule( true );

    // test whether the optional CharFormats are defined in this Document
    mpOutlineRule->CheckCharFormats( *this );

    // notify text nodes, which are registered at the outline style, about the
    // changed outline style
    SwNumRule::tTextNodeList aTextNodeList;
    mpOutlineRule->GetTextNodeList( aTextNodeList );
    for ( SwTextNode* pTextNd : aTextNodeList )
    {
        pTextNd->NumRuleChgd();

        // assure that list level corresponds to outline level
        if ( pTextNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle() &&
             pTextNd->GetAttrListLevel() != pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() )
        {
            pTextNd->SetAttrListLevel( pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() );
        }
    }

    PropagateOutlineRule();
    mpOutlineRule->Invalidate();
    UpdateNumRule();

    // update if we have foot notes && numbering by chapter
    if( !GetFootnoteIdxs().empty() && FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum )
        GetFootnoteIdxs().UpdateAllFootnote();

    getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().EndUndo(SwUndoId::OUTLINE_EDIT, nullptr);
    }

    getIDocumentState().SetModified();
}

void SwDoc::PropagateOutlineRule()
{
    SwNumRule* pMyOutlineRule = GetOutlineNumRule();
    if (!pMyOutlineRule)
        return;

    for (auto pColl : *mpTextFormatCollTable)
    {
        if(pColl->IsAssignedToListLevelOfOutlineStyle())
        {
            // Check only the list style, which is set at the paragraph style
            const SwNumRuleItem & rCollRuleItem = pColl->GetNumRule( false );

            if ( rCollRuleItem.GetValue().isEmpty() )
            {
                SwNumRuleItem aNumItem( pMyOutlineRule->GetName() );
                pColl->SetFormatAttr(aNumItem);
            }
        }
    }
}

// Increase/Decrease
bool SwDoc::OutlineUpDown(const SwPaM& rPam, short nOffset,
        SwRootFrame const*const pLayout)
{
    if( GetNodes().GetOutLineNds().empty() || !nOffset )
        return false;

    // calculate the range
    SwPaM aPam(rPam, nullptr);
    ExpandPamForParaPropsNodes(aPam, pLayout);
    const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
    SwNode* const pSttNd = &aPam.Start()->GetNode();
    SwNode* const pEndNd = &aPam.End()->GetNode();
    SwOutlineNodes::size_type nSttPos, nEndPos;

    if( !rOutlNds.Seek_Entry( pSttNd, &nSttPos ) &&
        !nSttPos-- )
        // we're not in an "Outline section"
        return false;

    if( rOutlNds.Seek_Entry( pEndNd, &nEndPos ) )
        ++nEndPos;

    // We now have the wanted range in the OutlineNodes array,
    // so check now if we're not invalidating sublevels
    // (stepping over the limits)

    // Here we go:
    // 1. Create the style array:
    SwTextFormatColl* aCollArr[ MAXLEVEL ];
    memset( aCollArr, 0, sizeof( SwTextFormatColl* ) * MAXLEVEL );

    forauto pTextFormatColl : *mpTextFormatCollTable )
    {
        if (pTextFormatColl->IsAssignedToListLevelOfOutlineStyle())
        {
            const int nLevel = pTextFormatColl->GetAssignedOutlineStyleLevel();
            aCollArr[ nLevel ] = pTextFormatColl;
        }
    }

    int n;

    /* Find the last occupied level (backward). */
    for (n = MAXLEVEL - 1; n > 0; n--)
    {
        if (aCollArr[n] != nullptr)
            break;
    }

    /* If an occupied level is found, choose next level (which IS
       unoccupied) until a valid level is found. If no occupied level
       was found n is 0 and aCollArr[0] is 0. In this case no demoting
       is possible. */

    if (aCollArr[n] != nullptr)
    {
        while (n < MAXLEVEL - 1)
        {
            n++;

            SwTextFormatColl *aTmpColl =
                getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));

            if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
                aTmpColl->GetAssignedOutlineStyleLevel() == n )
            {
                aCollArr[n] = aTmpColl;
                break;
            }
        }
    }

    /* Find the first occupied level (forward). */
    for (n = 0; n < MAXLEVEL - 1; n++)
    {
        if (aCollArr[n] != nullptr)
            break;
    }

    /* If an occupied level is found, choose previous level (which IS
       unoccupied) until a valid level is found. If no occupied level
       was found n is MAXLEVEL - 1 and aCollArr[MAXLEVEL - 1] is 0. In
       this case no demoting is possible. */

    if (aCollArr[n] != nullptr)
    {
        while (n > 0)
        {
            n--;

            SwTextFormatColl *aTmpColl =
                getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));

            if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
                aTmpColl->GetAssignedOutlineStyleLevel() == n )
            {
                aCollArr[n] = aTmpColl;
                break;
            }
        }
    }

    /* --> #i13747#

        Build a move table that states from which level to which other level
        an outline will be moved.

        the move table:
        aMoveArr[n] = m: replace aCollArr[n] with aCollArr[m]
    */

    int aMoveArr[MAXLEVEL];
    int nStep; // step size for searching in aCollArr: -1 or 1
    int nNum; // amount of steps for stepping in aCollArr

    if (nOffset < 0)
    {
        nStep = -1;
        nNum = -nOffset;
    }
    else
    {
        nStep = 1;
        nNum = nOffset;
    }

    /* traverse aCollArr */
    for (n = 0; n < MAXLEVEL; n++)
    {
        /* If outline level n has an assigned paragraph style step
           nNum steps forwards (nStep == 1) or backwards (nStep ==
           -1).  One step is to go to the next non-null entry in
           aCollArr in the selected direction. If nNum steps were
           possible write the index of the entry found to aCollArr[n],
           i.e. outline level n will be replaced by outline level
           aCollArr[n].

           If outline level n has no assigned paragraph style
           aMoveArr[n] is set to -1.
        */

        if (aCollArr[n] != nullptr)
        {
            int m = n;
            int nCount = nNum;

            while (nCount > 0 && m + nStep >= 0 && m + nStep < MAXLEVEL)
            {
                m += nStep;

                if (aCollArr[m] != nullptr)
                    nCount--;
            }

            if (nCount == 0)
                aMoveArr[n] = m;
            else
                aMoveArr[n] = -1;
        }
        else
            aMoveArr[n] = -1;
    }

    /* If moving of the outline levels is applicable, i.e. for all
       outline levels occurring in the document there has to be a valid
       target outline level implied by aMoveArr. */

    bool bMoveApplicable = true;
    for (auto i = nSttPos; i < nEndPos; ++i)
    {
        SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode();
        if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
        {
            continue;
        }
        SwTextFormatColl* pColl = pTextNd->GetTextColl();

        if( pColl->IsAssignedToListLevelOfOutlineStyle() )
        {
            const int nLevel = pColl->GetAssignedOutlineStyleLevel();
            if (aMoveArr[nLevel] == -1)
                bMoveApplicable = false;
        }

        // Check on outline level attribute of text node, if text node is
        // not an outline via a to outline style assigned paragraph style.
        else
        {
            const int nNewOutlineLevel = pTextNd->GetAttrOutlineLevel() + nOffset;
            if ( nNewOutlineLevel < 1 || nNewOutlineLevel > MAXLEVEL )
            {
                bMoveApplicable = false;
            }
        }
    }

    if (! bMoveApplicable )
        return false;

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().StartUndo(SwUndoId::OUTLINE_LR, nullptr);
        GetIDocumentUndoRedo().AppendUndo(
            std::make_unique<SwUndoOutlineLeftRight>(aPam, nOffset) );
    }

    // 2. Apply the new style to all Nodes
    for (auto i = nSttPos; i < nEndPos; ++i)
    {
        SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode();
        if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
        {
            continue;
        }
        SwTextFormatColl* pColl = pTextNd->GetTextColl();

        if( pColl->IsAssignedToListLevelOfOutlineStyle() )
        {
            const int nLevel = pColl->GetAssignedOutlineStyleLevel();

            OSL_ENSURE(aMoveArr[nLevel] >= 0,
                "move table: current TextColl not found when building table!");

            if (nLevel < MAXLEVEL && aMoveArr[nLevel] >= 0)
            {
                pColl = aCollArr[ aMoveArr[nLevel] ];

                if (pColl != nullptr)
                    pTextNd->ChgFormatColl( pColl );
            }

        }
        else if( pTextNd->GetAttrOutlineLevel() > 0)
        {
            int nLevel = pTextNd->GetAttrOutlineLevel() + nOffset;
            if( 0 <= nLevel && nLevel <= MAXLEVEL)
                pTextNd->SetAttrOutlineLevel( nLevel );

        }
        // Undo ???
    }
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().EndUndo(SwUndoId::OUTLINE_LR, nullptr);
    }

    ChkCondColls();
    getIDocumentState().SetModified();

    return true;
}

// Move up/down
bool SwDoc::MoveOutlinePara( const SwPaM& rPam,
                SwOutlineNodes::difference_type nOffset, const SwOutlineNodesInline* pOutlineNodesInline )
{
    // Do not move to special sections in the nodes array
    const SwPosition& rStt = *rPam.Start(),
                    & rEnd = *rPam.End();
    if( GetNodes().GetOutLineNds().empty() || !nOffset ||
        (rStt.GetNodeIndex() < GetNodes().GetEndOfExtras().GetIndex()) ||
        (rEnd.GetNodeIndex() < GetNodes().GetEndOfExtras().GetIndex()))
    {
        return false;
    }

    SwOutlineNodes::size_type nCurrentPos = 0;
    SwOutlineNodesInline::size_type nCurrentPosInline = 0;
    SwNodeIndex aSttRg( rStt.GetNode() ), aEndRg( rEnd.GetNode() );

    int nOutLineLevel = MAXLEVEL;
    SwNode* pSrch = &aSttRg.GetNode();

    if( pSrch->IsTextNode())
        nOutLineLevel = static_cast<sal_uInt8>(
                        pSrch->GetTextNode()->GetAttrOutlineLevel(/*bInlineHeading=*/true)-1);

    SwNode* pEndSrch = &aEndRg.GetNode();

    if( !pOutlineNodesInline && !GetNodes().GetOutLineNds().Seek_Entry( pSrch, &nCurrentPos ) )
    {
        if( !nCurrentPos )
            return false// Promoting or demoting before the first outline => no.
        assert(nCurrentPos > 0 && "coverity#1645558");
        if( --nCurrentPos )
            aSttRg = *GetNodes().GetOutLineNds()[ nCurrentPos ];
        else if( 0 > nOffset )
            return false// Promoting at the top of document?!
        else
            aSttRg = *GetNodes().GetEndOfContent().StartOfSectionNode();
    }
    else if ( pOutlineNodesInline )
    {
        if ( !pOutlineNodesInline->Seek_Entry_By_Anchor(pSrch, &nCurrentPosInline) )
        {
            if( !nCurrentPosInline )
                return false// Promoting or demoting before the first outline => no.
            assert(nCurrentPosInline > 0 && "coverity#1645558");
            if( --nCurrentPosInline )
            {
                aSttRg = *SwOutlineNodes::GetRootNode((*pOutlineNodesInline)[ nCurrentPosInline ]);
            }
            else if( 0 > nOffset )
                return false// Promoting at the top of document?!
            else
                aSttRg = *GetNodes().GetEndOfContent().StartOfSectionNode();
        }
    }
    SwOutlineNodes::size_type nTmpPos = 0;
    SwOutlineNodesInline::size_type nTmpPosInline = 0;
    // If the given range ends at an outlined text node we have to decide if it has to be a part of
    // the moving range or not. Normally it will be a sub outline of our chapter
    // and has to be moved, too. But if the chapter ends with a table(or a section end),
    // the next text node will be chosen and this could be the next outline of the same level.
    // The criteria has to be the outline level: sub level => incorporate, same/higher level => no.
    if( !pOutlineNodesInline && GetNodes().GetOutLineNds().Seek_Entry( pEndSrch, &nTmpPos ) )
    {
        if( !pEndSrch->IsTextNode() || pEndSrch == pSrch ||
            nOutLineLevel < pEndSrch->GetTextNode()->GetAttrOutlineLevel()-1 )
            ++nTmpPos; // For sub outlines only!
    }
    else if ( pOutlineNodesInline )
    {
        if ( pOutlineNodesInline->Seek_Entry_By_Anchor(pEndSrch, &nTmpPosInline) && (
            !pEndSrch->IsTextNode() || pEndSrch == pSrch || nOutLineLevel <
                pEndSrch->GetTextNode()->GetAttrOutlineLevel(/*bInlineHeading=*/true)-1 ) )
        {
            ++nTmpPosInline;
        }
    }

    if ( !pOutlineNodesInline )
    {
        aEndRg = nTmpPos < GetNodes().GetOutLineNds().size()
                    ? *GetNodes().GetOutLineNds()[ nTmpPos ]
                    : GetNodes().GetEndOfContent();
    }
    else
    {
        aEndRg = nTmpPosInline < pOutlineNodesInline->size()
                    ? *SwOutlineNodes::GetRootNode((*pOutlineNodesInline)[ nTmpPosInline ])
                    : GetNodes().GetEndOfContent();
    }

    if( nOffset >= 0 )
    {
        nCurrentPos = nTmpPos;
        nCurrentPosInline = nTmpPosInline;
    }
    if( aEndRg == aSttRg )
    {
        OSL_FAIL( "Moving outlines: Surprising selection" );
        ++aEndRg;
    }

    const SwNode* pNd;
    // The following code corrects the range to handle sections (start/end nodes)
    // The range will be extended if the least node before the range is a start node
    // which ends inside the range => The complete section will be moved.
    // The range will be shrunk if the last position is a start node.
    // The range will be shrunk if the last node is an end node which starts before the range.
    --aSttRg;
    while( aSttRg.GetNode().IsStartNode() )
    {
        pNd = aSttRg.GetNode().EndOfSectionNode();
        if( pNd->GetIndex() >= aEndRg.GetIndex() )
            break;
        --aSttRg;
    }
    ++aSttRg;

    --aEndRg;
    while( aEndRg.GetNode().IsStartNode() )
        --aEndRg;

    while( aEndRg.GetNode().IsEndNode() )
    {
        pNd = aEndRg.GetNode().StartOfSectionNode();
        if( pNd->GetIndex() >= aSttRg.GetIndex() )
            break;
        --aEndRg;
    }
    ++aEndRg;

    // calculation of the new position
    if( pOutlineNodesInline && nOffset < 0 && nCurrentPosInline < o3tl::make_unsigned(-nOffset) )
        pNd = GetNodes().GetEndOfContent().StartOfSectionNode();
    else if( pOutlineNodesInline && nCurrentPosInline + nOffset >= pOutlineNodesInline->size() )
        pNd = &GetNodes().GetEndOfContent();
    else if ( pOutlineNodesInline )
        pNd = SwOutlineNodes::GetRootNode((*pOutlineNodesInline)[ nCurrentPosInline + nOffset ]);
    else if( nOffset < 0 && nCurrentPos < o3tl::make_unsigned(-nOffset) )
        pNd = GetNodes().GetEndOfContent().StartOfSectionNode();
    else if( nCurrentPos + nOffset >= GetNodes().GetOutLineNds().size() )
        pNd = &GetNodes().GetEndOfContent();
    else
        pNd = GetNodes().GetOutLineNds()[ nCurrentPos + nOffset ];

    SwNodeOffset nNewPos = pNd->GetIndex();

    // And now a correction of the insert position if necessary...
    SwNodeIndex aInsertPos( *pNd, -1 );
    while( aInsertPos.GetNode().IsStartNode() )
    {
        // Just before the insert position starts a section:
        // when I'm moving forward I do not want to enter the section,
        // when I'm moving backward I want to stay in the section if I'm already a part of,
        // I want to stay outside if I was outside before.
        if( nOffset < 0 )
        {
            pNd = aInsertPos.GetNode().EndOfSectionNode();
            if( pNd->GetIndex() >= aEndRg.GetIndex() )
                break;
        }
        --aInsertPos;
        --nNewPos;
    }

    if( nOffset >= 0 )
    {
        // When just before the insert position a section ends, it is okay when I'm moving backward
        // because I want to stay outside the section.
        // When moving forward I've to check if I started inside or outside the section
        // because I don't want to enter of leave such a section
        while( aInsertPos.GetNode().IsEndNode() )
        {
            pNd = aInsertPos.GetNode().StartOfSectionNode();
            if( pNd->GetIndex() >= aSttRg.GetIndex() )
                break;
            --aInsertPos;
            --nNewPos;
        }
    }
    // We do not want to move into tables (at the moment)
    ++aInsertPos;
    pNd = &aInsertPos.GetNode();
    if( pNd->IsTableNode() )
        pNd = pNd->StartOfSectionNode();
    if( pNd->FindTableNode() )
        return false;

    OSL_ENSURE( aSttRg.GetIndex() > nNewPos || nNewPos >= aEndRg.GetIndex(),
                "Position lies within Move range" );

    // If a Position inside the special nodes array sections was calculated,
    // set it to document start instead.
    // Sections or Tables at the document start will be pushed backwards.
    nNewPos = std::max( nNewPos, GetNodes().GetEndOfExtras().GetIndex() + SwNodeOffset(2) );

    SwNodeOffset nOffs = nNewPos - ( 0 < nOffset ? aEndRg.GetIndex() : aSttRg.GetIndex());
    SwPaM aPam( aSttRg, aEndRg, SwNodeOffset(0), SwNodeOffset(-1) );
    return MoveParagraph( aPam, nOffs, true );
}

static SwTextNode* lcl_FindOutlineName(const SwOutlineNodes& rOutlNds,
    SwRootFrame const*const pLayout, std::u16string_view aName, bool const bExact)
{
    SwTextNode * pExactButDeleted(nullptr);
    SwTextNode* pSavedNode = nullptr;
    forauto pOutlNd : rOutlNds )
    {
        SwTextNode* pTextNd = pOutlNd->GetTextNode();
        const OUString sText( pTextNd->GetExpandText(pLayout) );
        if (sText.startsWith(aName))
        {
            if (sText.getLength() == sal_Int32(aName.size()))
            {
                if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
                {
                    pExactButDeleted = pTextNd;
                }
                else
                {
                    // Found "exact", set Pos to the Node
                    return pTextNd;
                }
            }
            if (!bExact && !pSavedNode
                && (!pLayout || sw::IsParaPropsNode(*pLayout, *pTextNd)))
            {
                // maybe we just found the text's first part
                pSavedNode = pTextNd;
            }
        }
    }

    return bExact ? pExactButDeleted : pSavedNode;
}

static SwTextNode* lcl_FindOutlineNum(const SwOutlineNodes& rOutlNds,
        OUString& rName, SwRootFrame const*const pLayout)
{
    // Valid numbers are (always just offsets!):
    //  ([Number]+\.)+  (as a regular expression!)
    //  (Number followed by a period, with 5 repetitions)
    //  i.e.: "1.1.", "1.", "1.1.1."
    sal_Int32 nPos = 0;
    std::u16string_view sNum = o3tl::getToken(rName, 0, '.', nPos );
    if( -1 == nPos )
        return nullptr;           // invalid number!

    sal_uInt16 nLevelVal[ MAXLEVEL ];       // numbers of all levels
    memset( nLevelVal, 0, MAXLEVEL * sizeof( nLevelVal[0] ));
    int nLevel = 0;
    std::u16string_view sName( rName );

    while( -1 != nPos )
    {
        sal_uInt16 nVal = 0;
        for( size_t n = 0; n < sNum.size(); ++n )
        {
            const sal_Unicode c {sNum[ n ]};
            if'0' <= c && c <= '9' )
            {
                nVal *= 10;
                nVal += c - '0';
            }
            else if( nLevel )
                break;                      // "almost" valid number
            else
                return nullptr;             // invalid number!
        }

        if( MAXLEVEL > nLevel )
            nLevelVal[ nLevel++ ] = nVal;

        sName = sName.substr( nPos );
        nPos = 0;
        sNum = o3tl::getToken(sName, 0, '.', nPos );
        // #i4533# without this check all parts delimited by a dot are treated as outline numbers
        if(!comphelper::string::isdigitAsciiString(sNum))
            break;
    }
    rName = sName;      // that's the follow-up text

    // read all levels, so search the document for this outline

    // Without OutlineNodes searching doesn't pay off
    // and we save a crash
    if( rOutlNds.empty() )
        return nullptr;

    // search in the existing outline nodes for the required outline num array
    forauto pOutlNd : rOutlNds )
    {
        SwTextNode* pNd = pOutlNd->GetTextNode();
        if ( pNd->GetAttrOutlineLevel() == nLevel )
        {
            // #i51089#, #i68289#
            // Assure, that text node has the correct numbering level. Otherwise,
            // its number vector will not fit to the searched level.
            if (pNd->GetNum(pLayout) && pNd->GetActualListLevel() == nLevel - 1)
            {
                const SwNodeNum & rNdNum = *(pNd->GetNum(pLayout));
                SwNumberTree::tNumberVector aLevelVal = rNdNum.GetNumberVector();
                // now compare with the one searched for
                bool bEqual = true;
                nLevel = std::min<int>(nLevel, MAXLEVEL);
                forint n = 0; n < nLevel; ++n )
                {
                    if ( aLevelVal[n] != nLevelVal[n] )
                    {
                        bEqual = false;
                        break;
                    }
                }
                if (bEqual)
                    return pNd;
            }
            else
            {
                // A text node, which has an outline paragraph style applied and
                // has as hard attribute 'no numbering' set, has an outline level,
                // but no numbering tree node. Thus, consider this situation in
                // the assertion condition.
                OSL_ENSURE( !pNd->GetNumRule(),
                        " - text node with outline level and numbering rule, but without numbering tree node. This is a serious defect" );
            }
        }
    }

    return nullptr;
}

// rName can contain a Number and/or the Text.
// First, we try to find the correct Entry via the Number.
// If it exists, we compare the Text to see if it's the right one.
// If that's not the case, we search again via the Text. If it is
// found, we got the right entry. Or else we use the one found by
// searching for the Number.
// If we don't have a Number, we search via the Text only.
bool SwDoc::GotoOutline(SwPosition& rPos, const OUString& rName, SwRootFrame const*const pLayout) const
{
    if( !rName.isEmpty() )
    {
        const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();

        // 1. step: via the Number:
        OUString sName( rName );
        SwTextNode* pNd = ::lcl_FindOutlineNum(rOutlNds, sName, pLayout);
        if ( pNd )
        {
            OUString sExpandedText = pNd->GetExpandText(pLayout);
            //#i4533# leading numbers followed by a dot have been remove while
            //searching for the outline position
            //to compensate this they must be removed from the paragraphs text content, too
            while(!sExpandedText.isEmpty())
            {
                sal_Int32 nPos = 0;
                std::u16string_view sTempNum = o3tl::getToken(sExpandedText, 0, '.', nPos);
                if( sTempNum.empty() || -1 == nPos ||
                    !comphelper::string::isdigitAsciiString(sTempNum))
                    break;
                sExpandedText = sExpandedText.copy(nPos);
            }

            if( sExpandedText != sName )
            {
                SwTextNode *pTmpNd = ::lcl_FindOutlineName(rOutlNds, pLayout, sName, true);
                if ( pTmpNd )             // found via the Name
                {
                    if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTmpNd))
                    {   // found the correct node but it's deleted!
                        return false// avoid fallback to inexact search
                    }
                    pNd = pTmpNd;
                }
            }
            rPos.Assign(*pNd);
            return true;
        }

        pNd = ::lcl_FindOutlineName(rOutlNds, pLayout, rName, false);
        if ( pNd )
        {
            rPos.Assign(*pNd);
            return true;
        }

        // #i68289# additional search on hyperlink URL without its outline numbering part
        if ( sName != rName )
        {
            pNd = ::lcl_FindOutlineName(rOutlNds, pLayout, sName, false);
            if ( pNd )
            {
                rPos.Assign(*pNd);
                return true;
            }
        }
    }
    return false;
}

static void lcl_ChgNumRule( SwDoc& rDoc, const SwNumRule& rRule )
{
    SwNumRule* pOld = rDoc.FindNumRulePtr( rRule.GetName() );
    if (!pOld) //we cannot proceed without the old NumRule
        return;

    sal_uInt16 nChgFormatLevel = 0;
    sal_uInt16 nMask = 1;

    for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n, nMask <<= 1 )
    {
        const SwNumFormat& rOldFormat = pOld->Get( n ), &rNewFormat = rRule.Get( n );

        if ( rOldFormat != rNewFormat )
        {
            nChgFormatLevel |= nMask;
        }
        else if ( SVX_NUM_NUMBER_NONE > rNewFormat.GetNumberingType()
                  && 1 < rNewFormat.GetIncludeUpperLevels()
                  && 0 != ( nChgFormatLevel & GetUpperLvlChg( n, rNewFormat.GetIncludeUpperLevels(), nMask ) ) )
        {
            nChgFormatLevel |= nMask;
        }
    }

    if( !nChgFormatLevel )         // Nothing has been changed?
    {
        const bool bInvalidateNumRule( pOld->IsContinusNum() != rRule.IsContinusNum() );
        pOld->CheckCharFormats( rDoc );
        pOld->SetContinusNum( rRule.IsContinusNum() );

        if ( bInvalidateNumRule )
        {
            pOld->Invalidate();
        }

        return ;
    }

    SwNumRule::tTextNodeList aTextNodeList;
    pOld->GetTextNodeList( aTextNodeList );
    sal_uInt8 nLvl( 0 );
    for ( SwTextNode* pTextNd : aTextNodeList )
    {
        nLvl = static_cast<sal_uInt8>(pTextNd->GetActualListLevel());

        if( nLvl < MAXLEVEL )
        {
            if( nChgFormatLevel & ( 1 << nLvl ))
            {
                pTextNd->NumRuleChgd();
            }
        }
    }

    for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
        if ( nChgFormatLevel & ( 1 << n ) )
            pOld->Set( n, rRule.GetNumFormat( n ) );

    pOld->CheckCharFormats( rDoc );
    pOld->Invalidate();
    pOld->SetContinusNum( rRule.IsContinusNum() );

    rDoc.UpdateNumRule();
}

OUString SwDoc::SetNumRule( const SwPaM& rPam,
                        const SwNumRule& rRule,
                        SetNumRuleMode eMode,
                        SwRootFrame const*const pLayout,
                        const OUString& sContinuedListId,
                        SvxTextLeftMarginItem const*const pTextLeftMarginToPropagate,
                        SvxFirstLineIndentItem const*const pFirstLineIndentToPropagate)
{
    OUString sListId;

    SwPaM aPam(rPam, nullptr);
    ExpandPamForParaPropsNodes(aPam, pLayout);

    SwUndoInsNum * pUndo = nullptr;
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        // Start/End for attributes!
        GetIDocumentUndoRedo().StartUndo( SwUndoId::INSNUM, nullptr );
        pUndo = new SwUndoInsNum( aPam, rRule );
        GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
    }

    SwNumRule* pNewOrChangedNumRule = FindNumRulePtr( rRule.GetName() );
    bool bNewNumRuleCreated = false;
    if ( pNewOrChangedNumRule == nullptr )
    {
        // create new numbering rule based on given one
        pNewOrChangedNumRule = ( *mpNumRuleTable )[MakeNumRule( rRule.GetName(), &rRule )];
        bNewNumRuleCreated = true;
    }
    else if ( rRule != *pNewOrChangedNumRule )
    {
        // change existing numbering rule
        if (pUndo)
        {
            pUndo->SaveOldNumRule( *pNewOrChangedNumRule );
        }
        ::lcl_ChgNumRule( *this, rRule );
        if (pUndo)
        {
            pUndo->SetLRSpaceEndPos();
        }
    }

    if (!(eMode & SetNumRuleMode::DontSetItem))
    {
        if (eMode & SetNumRuleMode::CreateNewList)
        {
            if ( bNewNumRuleCreated )
            {
                // apply list id of list, which has been created for the new list style
                sListId = pNewOrChangedNumRule->GetDefaultListId();
            }
            else
            {
                // create new list and apply its list id
                const SwList* pNewList = getIDocumentListsAccess().createList( OUString(), pNewOrChangedNumRule->GetName() );
                OSL_ENSURE( pNewList,
                        " - could not create new list. Serious defect." );
                sListId = pNewList->GetListId();
            }
        }
        else if ( !sContinuedListId.isEmpty() )
        {
            // apply given list id
            sListId = sContinuedListId;
        }
        if (!sListId.isEmpty())
        {
            getIDocumentContentOperations().InsertPoolItem(aPam,
                SfxStringItem(RES_PARATR_LIST_ID, sListId),
                SetAttrMode::DEFAULT, pLayout);
        }
    }

    if (!aPam.HasMark())
    {
        SwTextNode * pTextNd = aPam.GetPoint()->GetNode().GetTextNode();
        // robust code: consider case that the PaM doesn't denote a text node - e.g. it denotes a graphic node
        if ( pTextNd != nullptr )
        {
            assert(!pLayout || sw::IsParaPropsNode(*pLayout, *pTextNd));
            SwNumRule * pRule = pTextNd->GetNumRule();

            if (pRule && pRule->GetName() == pNewOrChangedNumRule->GetName())
            {
                eMode |= SetNumRuleMode::DontSetItem;
                if ( !pTextNd->IsInList() )
                {
                    pTextNd->AddToList();
                }
            }
            // Only clear numbering attribute at text node, if at paragraph
            // style the new numbering rule is found.
            else if ( !pRule )
            {
                SwTextFormatColl* pColl = pTextNd->GetTextColl();
                if ( pColl )
                {
                    SwNumRule* pCollRule = FindNumRulePtr(pColl->GetNumRule().GetValue());
                    if ( pCollRule && pCollRule->GetName() == pNewOrChangedNumRule->GetName() )
                    {
                        pTextNd->ResetAttr( RES_PARATR_NUMRULE );
                        eMode |= SetNumRuleMode::DontSetItem;
                    }
                }
            }
        }
    }

    if (!(eMode & SetNumRuleMode::DontSetItem))
    {
        if (eMode & SetNumRuleMode::DontSetIfAlreadyApplied)
        {
            for (SwNodeIndex i = aPam.Start()->nNode; i <= aPam.End()->nNode; ++i)
            {
                if (SwTextNode const*const pNode = i.GetNode().GetTextNode())
                {
                    if (pNode->GetNumRule(true) != pNewOrChangedNumRule)
                    {
                        // only apply if it doesn't already have it - to
                        // avoid overriding indents from style
                        SwPaM const temp(*pNode, 0, *pNode, pNode->Len());
                        getIDocumentContentOperations().InsertPoolItem(temp,
                                SwNumRuleItem(pNewOrChangedNumRule->GetName()),
                                SetAttrMode::DEFAULT, pLayout);
                        // apply provided margins to get visually same result
                        if (pTextLeftMarginToPropagate)
                        {
                            getIDocumentContentOperations().InsertPoolItem(temp,
                                    *pTextLeftMarginToPropagate,
                                    SetAttrMode::DEFAULT, pLayout);
                        }
                        if (pFirstLineIndentToPropagate)
                        {
                            getIDocumentContentOperations().InsertPoolItem(temp,
                                    *pFirstLineIndentToPropagate,
                                    SetAttrMode::DEFAULT, pLayout);
                        }
                    }
                }
            }
        }
        else
        {
            getIDocumentContentOperations().InsertPoolItem(aPam,
                    SwNumRuleItem(pNewOrChangedNumRule->GetName()),
                    SetAttrMode::DEFAULT, pLayout);
            if (pTextLeftMarginToPropagate)
            {
                getIDocumentContentOperations().InsertPoolItem(aPam,
                        *pTextLeftMarginToPropagate,
                        SetAttrMode::DEFAULT, pLayout);
            }
            if (pFirstLineIndentToPropagate)
            {
                getIDocumentContentOperations().InsertPoolItem(aPam,
                        *pFirstLineIndentToPropagate,
                        SetAttrMode::DEFAULT, pLayout);
            }
        }
    }

    if ((eMode & SetNumRuleMode::ResetIndentAttrs)
         && pNewOrChangedNumRule->Get( 0 ).GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
    {
        const o3tl::sorted_vector<sal_uInt16> attrs{ RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT, RES_MARGIN_RIGHT };
        ::lcl_ResetIndentAttrs(this, aPam, attrs, pLayout);
    }

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().EndUndo( SwUndoId::INSNUM, nullptr );
    }

    getIDocumentState().SetModified();

    return sListId;
}

void SwDoc::SetCounted(const SwPaM & rPam, bool bCounted,
        SwRootFrame const*const pLayout)
{
    if ( bCounted )
    {
        const o3tl::sorted_vector<sal_uInt16> attrs{ RES_PARATR_LIST_ISCOUNTED };
        ::lcl_ResetIndentAttrs(this, rPam, attrs, pLayout);
    }
    else
    {
        getIDocumentContentOperations().InsertPoolItem(rPam,
                SfxBoolItem(RES_PARATR_LIST_ISCOUNTED, false),
                SetAttrMode::DEFAULT, pLayout);
    }
}

void SwDoc::SetNumRuleStart( const SwPosition& rPos, bool bFlag )
{
    SwTextNode* pTextNd = rPos.GetNode().GetTextNode();

    if (!pTextNd)
        return;

    const SwNumRule* pRule = pTextNd->GetNumRule();
    if( pRule && !bFlag != !pTextNd->IsListRestart())
    {
        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().AppendUndo(
                std::make_unique<SwUndoNumRuleStart>(rPos, bFlag) );
        }

        pTextNd->SetListRestart(bFlag);

        getIDocumentState().SetModified();
    }
}

void SwDoc::SetNodeNumStart( const SwPosition& rPos, sal_uInt16 nStt )
{
    SwTextNode* pTextNd = rPos.GetNode().GetTextNode();

    if (!pTextNd)
        return;

    if ( !pTextNd->HasAttrListRestartValue() ||
         pTextNd->GetAttrListRestartValue() != nStt )
    {
        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().AppendUndo(
                std::make_unique<SwUndoNumRuleStart>(rPos, nStt) );
        }
        pTextNd->SetAttrListRestartValue( nStt );

        getIDocumentState().SetModified();
    }
}

// We can only delete if the Rule is unused!
bool SwDoc::DelNumRule( const UIName& rName, bool bBroadcast )
{
    sal_uInt16 nPos = FindNumRule( rName );

    if (nPos == USHRT_MAX)
        return false;

    if ( (*mpNumRuleTable)[ nPos ] == GetOutlineNumRule() )
    {
        OSL_FAIL( " - No deletion of outline list style. This is serious defect" );
        return false;
    }

    if( !IsUsed( *(*mpNumRuleTable)[ nPos ] ))
    {
        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().AppendUndo(
                std::make_unique<SwUndoNumruleDelete>(*(*mpNumRuleTable)[nPos], *this));
        }

        if (bBroadcast)
            BroadcastStyleOperation(rName, SfxStyleFamily::Pseudo,
                                    SfxHintId::StyleSheetErased);

        getIDocumentListsAccess().deleteListForListStyle( rName );
        getIDocumentListsAccess().deleteListsByDefaultListStyle( rName );
        // #i34097# DeleteAndDestroy deletes rName if
        // rName is directly taken from the numrule.
        const UIName aTmpName( rName );
        delete (*mpNumRuleTable)[ nPos ];
        mpNumRuleTable->erase( mpNumRuleTable->begin() + nPos );
        maNumRuleMap.erase(aTmpName);

        getIDocumentState().SetModified();
        return true;
    }
    return false;
}

void SwDoc::ChgNumRuleFormats( const SwNumRule& rRule )
{
    SwNumRule* pRule = FindNumRulePtr( rRule.GetName() );
    if( !pRule )
        return;

    SwUndoInsNum* pUndo = nullptr;
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        pUndo = new SwUndoInsNum( *pRule, rRule, *this );
        pUndo->GetHistory();
        GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
    }
    ::lcl_ChgNumRule( *this, rRule );
    if (pUndo)
    {
        pUndo->SetLRSpaceEndPos();
    }

    getIDocumentState().SetModified();
}

bool SwDoc::RenameNumRule(const UIName & rOldName, const UIName & rNewName,
                              bool bBroadcast)
{
    assert(!FindNumRulePtr(rNewName));

    bool bResult = false;
    SwNumRule * pNumRule = FindNumRulePtr(rOldName);

    if (pNumRule)
    {
        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().AppendUndo(
                std::make_unique<SwUndoNumruleRename>(rOldName, rNewName, *this));
        }

        SwNumRule::tTextNodeList aTextNodeList;
        pNumRule->GetTextNodeList( aTextNodeList );

        pNumRule->SetName( rNewName, getIDocumentListsAccess() );
        SwNumRuleItem aItem(rNewName);

        const size_t nArrLen = GetTextFormatColls()->size();
        for( size_t i = 0; i < nArrLen; i++ )
        {
            SwTextFormatColl* pColl = (*GetTextFormatColls())[ i ];
            const SwAttrSet& rAttrSet = pColl->GetAttrSet();

            const SfxPoolItem* pTempItem = nullptr;
            if (SfxItemState::SET == rAttrSet.GetItemState(RES_PARATR_NUMRULE, false, &pTempItem))
            {
                const SwNumRuleItem* pNumItem = static_cast<const SwNumRuleItem*>(pTempItem);
                if (pNumItem->GetValue() == rOldName)
                    pColl->SetFormatAttr( aItem );
            }
        }

        for ( SwTextNode* pTextNd : aTextNodeList )
        {
            if (SfxItemState::SET == pTextNd->GetSwAttrSet().GetItemState(RES_PARATR_NUMRULE, false))
                pTextNd->SetAttr(aItem);
        }

        bResult = true;

        if (bBroadcast)
            BroadcastStyleOperation(rOldName, SfxStyleFamily::Pseudo,
                                    SfxHintId::StyleSheetModified);
    }

    return bResult;
}

void SwDoc::StopNumRuleAnimations( const OutputDevice* pOut )
{
    for( sal_uInt16 n = GetNumRuleTable().size(); n; )
    {
        SwNumRule::tTextNodeList aTextNodeList;
        GetNumRuleTable()[ --n ]->GetTextNodeList( aTextNodeList );
        for ( SwTextNode* pTNd : aTextNodeList )
        {
            SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTNd);
            for(SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
                if (pFrame->HasAnimation() &&
                    (!pFrame->GetMergedPara() || pFrame->GetMergedPara()->pParaPropsNode == pTNd))
                {
                    pFrame->StopAnimation( pOut );
                }
        }
    }
}

void SwDoc::ReplaceNumRule( const SwPosition& rPos,
                            const UIName& rOldRule, const UIName& rNewRule )
{
    SwNumRule *pOldRule = FindNumRulePtr( rOldRule ),
              *pNewRule = FindNumRulePtr( rNewRule );
    if( !pOldRule || !pNewRule || pOldRule == pNewRule )
        return;

    SwUndoInsNum* pUndo = nullptr;
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        // Start/End for attributes!
        GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
        pUndo = new SwUndoInsNum( rPos, *pNewRule, rOldRule );
        GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
    }

    SwNumRule::tTextNodeList aTextNodeList;
    pOldRule->GetTextNodeList( aTextNodeList );
    if ( !aTextNodeList.empty() )
    {
        SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );

        const SwTextNode* pGivenTextNode = rPos.GetNode().GetTextNode();
        SwNumRuleItem aRule( rNewRule );
        for ( SwTextNode* pTextNd : aTextNodeList )
        {
            if ( pGivenTextNode &&
                 pGivenTextNode->GetListId() == pTextNd->GetListId() )
            {
                aRegH.RegisterInModify( pTextNd, *pTextNd );

                pTextNd->SetAttr( aRule );
                pTextNd->NumRuleChgd();
            }
        }
        GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
        getIDocumentState().SetModified();
    }
}

namespace
{
    struct ListStyleData
    {
        SwNumRule* pReplaceNumRule;
        bool bCreateNewList;
        OUString sListId;

        ListStyleData()
            : pReplaceNumRule( nullptr ),
              bCreateNewList( false )
        {}
    };
}

void SwDoc::MakeUniqueNumRules(const SwPaM & rPaM)
{
    OSL_ENSURE( &rPaM.GetDoc() == this"need same doc" );

    std::map<SwNumRule *, ListStyleData> aMyNumRuleMap;

    bool bFirst = true;

    const SwNodeOffset nStt = rPaM.Start()->GetNodeIndex();
    const SwNodeOffset nEnd = rPaM.End()->GetNodeIndex();
    for (SwNodeOffset n = nStt; n <= nEnd; n++)
    {
        SwTextNode * pCNd = GetNodes()[n]->GetTextNode();

        if (pCNd)
        {
            SwNumRule * pRule = pCNd->GetNumRule();

            if (pRule && pRule->IsAutoRule() && ! pRule->IsOutlineRule())
            {
                ListStyleData aListStyleData = aMyNumRuleMap[pRule];

                if ( aListStyleData.pReplaceNumRule == nullptr )
                {
                    if (bFirst)
                    {
                        SwPosition aPos(*pCNd);
                        aListStyleData.pReplaceNumRule =
                            const_cast<SwNumRule *>
                            (SearchNumRule( aPos, false, pCNd->HasNumber(),
                                            false, 0,
                                    aListStyleData.sListId, nullptr, true ));
                    }

                    if ( aListStyleData.pReplaceNumRule == nullptr )
                    {
                        aListStyleData.pReplaceNumRule = new SwNumRule(*pRule);
                        aListStyleData.pReplaceNumRule->SetName( GetUniqueNumRuleName(), getIDocumentListsAccess() );
                        aListStyleData.bCreateNewList = true;
                    }

                    aMyNumRuleMap[pRule] = aListStyleData;
                }

                SwPaM aPam(*pCNd);

                SetNumRule( aPam,
                            *aListStyleData.pReplaceNumRule,
                            aListStyleData.bCreateNewList ? SetNumRuleMode::CreateNewList : SetNumRuleMode::Default,
                            nullptr,
                            aListStyleData.sListId );
                if ( aListStyleData.bCreateNewList )
                {
                    aListStyleData.bCreateNewList = false;
                    aListStyleData.sListId = pCNd->GetListId();
                    aMyNumRuleMap[pRule] = std::move(aListStyleData);
                }

                bFirst = false;
            }
        }
    }
}

bool SwDoc::NoNum( const SwPaM& rPam )
{

    bool bRet = getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), false );
    // Do we actually use Numbering at all?
    if( bRet )
    {
        // Set NoNum and Update
        SwTextNode* pNd = rPam.GetPoint()->GetNode().GetTextNode();
        const SwNumRule* pRule = pNd->GetNumRule();
        if( pRule )
        {
            pNd->SetCountedInList(false);

            getIDocumentState().SetModified();
        }
        else
            bRet = false;   // no Numbering or just always true?
    }
    return bRet;
}

void SwDoc::DelNumRules(const SwPaM& rPam, SwRootFrame const*const pLayout)
{
    SwPaM aPam(rPam, nullptr);
    ExpandPamForParaPropsNodes(aPam, pLayout);
    SwNodeOffset nStt = aPam.Start()->GetNodeIndex();
    SwNodeOffset const nEnd = aPam.End()->GetNodeIndex();

    SwUndoDelNum* pUndo;
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        pUndo = new SwUndoDelNum( aPam );
        GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
    }
    else
        pUndo = nullptr;

    SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );

    SwNumRuleItem aEmptyRule;
    const SwNode* pOutlNd = nullptr;
    for( ; nStt <= nEnd; ++nStt )
    {
        SwTextNode* pTNd = GetNodes()[ nStt ]->GetTextNode();
        if (pLayout && pTNd)
        {
            pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
        }
        SwNumRule* pNumRuleOfTextNode = pTNd ? pTNd->GetNumRule() : nullptr;
        if ( pTNd && pNumRuleOfTextNode )
        {
            // recognize changes of attribute for undo
            aRegH.RegisterInModify( pTNd, *pTNd );

            if( pUndo )
                pUndo->AddNode( *pTNd );

            // directly set list style attribute is reset, otherwise empty
            // list style is applied
            const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet();
            if ( pAttrSet &&
                 pAttrSet->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
                pTNd->ResetAttr( RES_PARATR_NUMRULE );
            else
                pTNd->SetAttr( aEmptyRule );

            pTNd->ResetAttr( RES_PARATR_LIST_ID );
            pTNd->ResetAttr( RES_PARATR_LIST_LEVEL );
            pTNd->ResetAttr( RES_PARATR_LIST_ISRESTART );
            pTNd->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
            pTNd->ResetAttr( RES_PARATR_LIST_ISCOUNTED );

            if( RES_CONDTXTFMTCOLL == pTNd->GetFormatColl()->Which() )
            {
                pTNd->ChkCondColl();
            }
            else
            {
                auto pParaStyle = static_cast<SwTextFormatColl*>(pTNd->GetFormatColl());
                if (pParaStyle && pParaStyle->IsAssignedToListLevelOfOutlineStyle())
                {
                    if (!pOutlNd)
                        pOutlNd = pTNd;
                    pTNd->SetCountedInList(false);
                }
            }
        }
    }

    // Finally, update all
    UpdateNumRule();

    if( pOutlNd )
        GetNodes().UpdateOutlineIdx( *pOutlNd );
}

void SwDoc::InvalidateNumRules()
{
    for (size_t n = 0; n < mpNumRuleTable->size(); ++n)
        (*mpNumRuleTable)[n]->Invalidate();
}

// To the next/preceding Bullet at the same Level
static bool lcl_IsNumOk( sal_uInt8 nSrchNum, sal_uInt8& rLower, sal_uInt8& rUpper,
                    bool bOverUpper, sal_uInt8 nNumber )
{
    OSL_ENSURE( nNumber < MAXLEVEL,
            " - misusage of method" );

    bool bRet = false;
    {
        if( bOverUpper ? nSrchNum == nNumber : nSrchNum >= nNumber )
            bRet = true;
        else if( nNumber > rLower )
            rLower = nNumber;
        else if( nNumber < rUpper )
            rUpper = nNumber;
    }
    return bRet;
}

static bool lcl_IsValidPrevNextNumNode( const SwNodeIndex& rIdx )
{
    bool bRet = false;
    const SwNode& rNd = rIdx.GetNode();
    switch( rNd.GetNodeType() )
    {
    case SwNodeType::End:
        bRet = SwTableBoxStartNode == rNd.StartOfSectionNode()->GetStartNodeType() ||
                rNd.StartOfSectionNode()->IsSectionNode();
        break;

    case SwNodeType::Start:
        bRet = SwTableBoxStartNode == static_cast<const SwStartNode&>(rNd).GetStartNodeType();
        break;

    case SwNodeType::Section:            // that one's valid, so proceed
        bRet = true;
        break;

    defaultbreak;
    }
    return bRet;
}

namespace sw {

void
GotoPrevLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const*const pLayout)
{
    if (pLayout && pLayout->HasMergedParas())
    {
        if (rIndex.GetNode().IsTextNode())
        {
            if (rIndex.GetNode().GetRedlineMergeFlag() != SwNode::Merge::None)
            {
                // not a tracked row deletion in Hide Changes mode
                if (SwContentFrame* pFrame = rIndex.GetNode().GetTextNode()->getLayoutFrame(pLayout))
                {
                    if (sw::MergedPara* pMerged = static_cast<SwTextFrame*>(pFrame)->GetMergedPara())
                    {
                        rIndex = pMerged->pFirstNode->GetIndex();
                    }
                }
            }
        }
        else if (rIndex.GetNode().IsEndNode())
        {
            if (rIndex.GetNode().GetRedlineMergeFlag() == SwNode::Merge::Hidden)
            {
                rIndex = *rIndex.GetNode().StartOfSectionNode();
                assert(rIndex.GetNode().IsTableNode());
            }
        }
    }
    --rIndex;
    if (pLayout && rIndex.GetNode().IsTextNode())
    {
        rIndex = *sw::GetParaPropsNode(*pLayout, *rIndex.GetNode().GetTextNode());
    }
}

void
GotoNextLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const*const pLayout)
{
    if (pLayout && pLayout->HasMergedParas())
    {
        if (rIndex.GetNode().IsTextNode())
        {
            if (rIndex.GetNode().GetRedlineMergeFlag() != SwNode::Merge::None)
            {
                if (SwContentFrame* pFrame = rIndex.GetNode().GetTextNode()->getLayoutFrame(pLayout))
                {
                    if (sw::MergedPara* pMerged = static_cast<SwTextFrame*>(pFrame)->GetMergedPara())
                    {
                        rIndex = pMerged->pLastNode->GetIndex();
                    }
                }
            }
        }
        else if (rIndex.GetNode().IsTableNode())
        {
            if (rIndex.GetNode().GetRedlineMergeFlag() == SwNode::Merge::Hidden)
            {
                rIndex = *rIndex.GetNode().EndOfSectionNode();
            }
        }
    }
    ++rIndex;
    if (pLayout && rIndex.GetNode().IsTextNode())
    {
        rIndex = *sw::GetParaPropsNode(*pLayout, *rIndex.GetNode().GetTextNode());
    }
}

// namespace sw

static bool lcl_GotoNextPrevNum( SwPosition& rPos, bool bNext,
        bool bOverUpper, sal_uInt8* pUpper, sal_uInt8* pLower,
        SwRootFrame const*const pLayout)
{
    const SwTextNode* pNd = rPos.GetNode().GetTextNode();
    if (pNd && pLayout)
    {
        pNd = sw::GetParaPropsNode(*pLayout, *pNd);
    }
    if( !pNd || nullptr == pNd->GetNumRule() )
        return false;

    sal_uInt8 nSrchNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());

    SwNodeIndex aIdx( rPos.GetNode() );
    if( ! pNd->IsCountedInList() )
    {
        bool bError = false;
        do {
            sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
            if( aIdx.GetNode().IsTextNode() )
            {
                pNd = aIdx.GetNode().GetTextNode();
                const SwNumRule* pRule = pNd->GetNumRule();

                if( pRule  )
                {
                    sal_uInt8 nTmpNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());
                    if( pNd->IsCountedInList() || (nTmpNum < nSrchNum ) )
                        break;      // found it!
                }
                else
                    bError = true;
            }
            else
                bError = !lcl_IsValidPrevNextNumNode( aIdx );

        } while( !bError );
        if( bError )
            return false;
    }

    sal_uInt8 nLower = nSrchNum, nUpper = nSrchNum;
    bool bRet = false;

    const SwTextNode* pLast;
    if( bNext )
    {
        sw::GotoNextLayoutTextFrame(aIdx, pLayout);
        pLast = pNd;
    }
    else
    {
        sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
        pLast = nullptr;
    }

    while( bNext ? ( aIdx.GetIndex() < aIdx.GetNodes().Count() - 1 )
                 : aIdx.GetIndex() != SwNodeOffset(0) )
    {
        if( aIdx.GetNode().IsTextNode() )
        {
            pNd = aIdx.GetNode().GetTextNode();
            const SwNumRule* pRule = pNd->GetNumRule();
            if( pRule )
            {
                if( ::lcl_IsNumOk( nSrchNum, nLower, nUpper, bOverUpper,
                                    static_cast<sal_uInt8>(pNd->GetActualListLevel()) ))
                {
                    rPos.Assign(aIdx);
                    bRet = true;
                    break;
                }
                else
                    pLast = pNd;
            }
            else
                break;
        }
        else if( !lcl_IsValidPrevNextNumNode( aIdx ))
            break;

        if( bNext )
            sw::GotoNextLayoutTextFrame(aIdx, pLayout);
        else
            sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
    }

    if( !bRet && !bOverUpper && pLast )     // do not iterate over higher numbers, but still to the end
    {
        if( bNext )
            rPos.Assign(aIdx);
        else
            rPos.Assign( *pLast );
        bRet = true;
    }

    if( bRet )
    {
        if( pUpper )
            *pUpper = nUpper;
        if( pLower )
            *pLower = nLower;
    }
    return bRet;
}

bool SwDoc::GotoNextNum(SwPosition& rPos, SwRootFrame const*const pLayout,
        bool bOverUpper, sal_uInt8* pUpper, sal_uInt8* pLower)
{
    return ::lcl_GotoNextPrevNum(rPos, true, bOverUpper, pUpper, pLower, pLayout);
}

const SwNumRule *  SwDoc::SearchNumRule(const SwPosition & rPos,
                                        const bool bForward,
                                        const bool bNum,
                                        const bool bOutline,
                                        int nNonEmptyAllowed,
                                        OUString& sListId,
                                        SwRootFrame const* pLayout,
                                        const bool bInvestigateStartNode,
                                        SvxTextLeftMarginItem const** o_ppTextLeftMargin,
                                        SvxFirstLineIndentItem const** o_ppFirstLineIndent)
{
    const SwNumRule * pResult = nullptr;
    SwTextNode * pTextNd = rPos.GetNode().GetTextNode();
    if (pLayout)
    {
        pTextNd = sw::GetParaPropsNode(*pLayout, rPos.GetNode());
    }
    SwNode * pStartFromNode = pTextNd;

    if (pTextNd)
    {
        SwNodeIndex aIdx(rPos.GetNode());

        // - the start node has also been investigated, if requested.
        const SwNode * pNode = nullptr;
        do
        {
            if ( !bInvestigateStartNode )
            {
                if (bForward)
                    sw::GotoNextLayoutTextFrame(aIdx, pLayout);
                else
                    sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
            }

            if (aIdx.GetNode().IsTextNode())
            {
                pTextNd = aIdx.GetNode().GetTextNode();

                const SwNumRule * pNumRule = pTextNd->GetNumRule();
                if (pNumRule)
                {
                    if ( ( pNumRule->IsOutlineRule() == bOutline ) &&
                         ( ( bNum && pNumRule->Get(0).IsEnumeration()) ||
                           ( !bNum && pNumRule->Get(0).IsItemize() ) ) ) // #i22362#, #i29560#
                    {
                        pResult = pNumRule;
                        // provide also the list id, to which the text node belongs.
                        sListId = pTextNd->GetListId();
                        // also get the margins that override the numrule
                        int const nListLevel{pTextNd->GetActualListLevel()};
                        if ((o_ppTextLeftMargin || o_ppFirstLineIndent)
                            && 0 <= nListLevel
                            && pNumRule->Get(o3tl::narrowing<sal_uInt16>(nListLevel))
                                .GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT)
                        {
                            ::sw::ListLevelIndents const indents{pTextNd->AreListLevelIndentsApplicable()};
                            if (!(indents & ::sw::ListLevelIndents::LeftMargin)
                                && o_ppTextLeftMargin)
                            {
                                *o_ppTextLeftMargin = &pTextNd->SwContentNode::GetAttr(RES_MARGIN_TEXTLEFT);
                            }
                            if (!(indents & ::sw::ListLevelIndents::FirstLine)
                                && o_ppFirstLineIndent)
                            {
                                *o_ppFirstLineIndent = &pTextNd->SwContentNode::GetAttr(RES_MARGIN_FIRSTLINE);
                            }
                        }
                    }

                    break;
                }
                else if (pTextNd->Len() > 0 || nullptr != pTextNd->GetNumRule())
                {
                    if (nNonEmptyAllowed == 0)
                        break;

                    nNonEmptyAllowed--;

                    if (nNonEmptyAllowed < 0)
                        nNonEmptyAllowed = -1;
                }
            }

            if ( bInvestigateStartNode )
            {
                if (bForward)
                    sw::GotoNextLayoutTextFrame(aIdx, pLayout);
                else
                    sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
            }

            pNode = &aIdx.GetNode();
        }
        while (pNode != GetNodes().DocumentSectionStartNode(pStartFromNode) &&
                 pNode != GetNodes().DocumentSectionEndNode(pStartFromNode));
    }

    return pResult;
}

bool SwDoc::GotoPrevNum(SwPosition& rPos, SwRootFrame const*const pLayout,
        bool bOverUpper)
{
    return ::lcl_GotoNextPrevNum(rPos, false, bOverUpper, nullptr, nullptr, pLayout);
}

bool SwDoc::NumUpDown(const SwPaM& rPam, bool bDown, SwRootFrame const*const pLayout)
{
    SwPaM aPam(rPam, nullptr);
    ExpandPamForParaPropsNodes(aPam, pLayout);
    SwNodeOffset nStt = aPam.Start()->GetNodeIndex();
    SwNodeOffset const nEnd = aPam.End()->GetNodeIndex();

    // -> outline nodes are promoted or demoted differently
    bool bOnlyOutline = true;
    bool bOnlyNonOutline = true;
    for (SwNodeOffset n = nStt; n <= nEnd; n++)
    {
        SwTextNode * pTextNd = GetNodes()[n]->GetTextNode();

        if (pTextNd)
        {
            if (pLayout)
            {
                pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd);
            }
            SwNumRule * pRule = pTextNd->GetNumRule();

            if (pRule)
            {
                if (pRule->IsOutlineRule())
                    bOnlyNonOutline = false;
                else
                    bOnlyOutline = false;
            }
        }
    }

    bool bRet = true;
    sal_Int8 nDiff = bDown ? 1 : -1;

    if (bOnlyOutline)
        bRet = OutlineUpDown(rPam, nDiff, pLayout);
    else if (bOnlyNonOutline)
    {
        /* #i24560#
        Only promote or demote if all selected paragraphs are
        promotable resp. demotable.
        */

        for (SwNodeOffset nTmp = nStt; nTmp <= nEnd; ++nTmp)
        {
            SwTextNode* pTNd = GetNodes()[ nTmp ]->GetTextNode();

            // Make code robust: consider case that the node doesn't denote a
            // text node.
            if ( pTNd )
            {
                if (pLayout)
                {
                    pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
                }

                SwNumRule * pRule = pTNd->GetNumRule();

                if (pRule)
                {
                    sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
                    if( (-1 == nDiff && 0 >= nLevel) ||
                        (1 == nDiff && MAXLEVEL - 1 <= nLevel))
                        bRet = false;
                }
            }
        }

        if( bRet )
        {
            if (GetIDocumentUndoRedo().DoesUndo())
            {
                GetIDocumentUndoRedo().AppendUndo(
                    std::make_unique<SwUndoNumUpDown>(aPam, nDiff) );
            }

            SwTextNode* pPrev = nullptr;
            for(SwNodeOffset nTmp = nStt; nTmp <= nEnd; ++nTmp )
            {
                SwTextNode* pTNd = GetNodes()[ nTmp ]->GetTextNode();

                if( pTNd)
                {
                    if (pLayout)
                    {
                        pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
                        if (pTNd == pPrev)
                        {
                            continue;
                        }
                        pPrev = pTNd;
                    }

                    SwNumRule * pRule = pTNd->GetNumRule();

                    if (pRule)
                    {
                        sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
                        nLevel = nLevel + nDiff;

                        pTNd->SetAttrListLevel(nLevel);
                    }
                }
            }

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

--> maximum size reached

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

Messung V0.5
C=91 H=86 G=88

¤ Dauer der Verarbeitung: 0.21 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.