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

Quelle  swtable.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 <libxml/xmlwriter.h>

#include <hintids.hxx>
#include <hints.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/colritem.hxx>
#include <osl/diagnose.h>
#include <sfx2/linkmgr.hxx>
#include <fmtfsize.hxx>
#include <fmtornt.hxx>
#include <fmtpdsc.hxx>
#include <fldbas.hxx>
#include <fmtfld.hxx>
#include <frmatr.hxx>
#include <doc.hxx>
#include <IDocumentLinksAdministration.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <docary.hxx>
#include <frame.hxx>
#include <swtable.hxx>
#include <ndtxt.hxx>
#include <tabcol.hxx>
#include <tabfrm.hxx>
#include <cellfrm.hxx>
#include <rowfrm.hxx>
#include <swserv.hxx>
#include <expfld.hxx>
#include <mdiexp.hxx>
#include <cellatr.hxx>
#include <txatbase.hxx>
#include <htmltbl.hxx>
#include <swtblfmt.hxx>
#include <ndindex.hxx>
#include <tblrwcl.hxx>
#include <shellres.hxx>
#include <viewsh.hxx>
#include <redline.hxx>
#include <vector>
#include <calbck.hxx>
#include <o3tl/string_view.hxx>
#include <svl/numformat.hxx>
#include <txtfld.hxx>
#include <rolbck.hxx>

#ifdef DBG_UTIL
#define CHECK_TABLE(t) (t).CheckConsistency();
#else
#define CHECK_TABLE(t)
#endif

using namespace com::sun::star;


#define COLFUZZY 20

static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
                    bool bChgAlign, SwNodeOffset nNdPos );

void SwTableBox::setRowSpan( sal_Int32 nNewRowSpan )
{
    mnRowSpan = nNewRowSpan;
}

bool SwTableBox::getDummyFlag() const
{
    return mbDummyFlag;
}

void SwTableBox::setDummyFlag( bool bDummy )
{
    mbDummyFlag = bDummy;
}

//JP 15.09.98: Bug 55741 - Keep tabs (front and rear)
static void lcl_TabToBlankAtSttEnd( OUString& rText )
{
    sal_Unicode c;
    sal_Int32 n;

    for( n = 0; n < rText.getLength() && ' ' >= ( c = rText[n] ); ++n )
        if'\x9' == c )
            rText = rText.replaceAt( n, 1, u" " );
    for( n = rText.getLength(); n && ' ' >= ( c = rText[--n] ); )
        if'\x9' == c )
            rText = rText.replaceAt( n, 1, u" " );
}

static void lcl_DelTabsAtSttEnd( OUString& rText )
{
    sal_Unicode c;
    sal_Int32 n;
    OUStringBuffer sBuff(rText);

    for( n = 0; n < sBuff.getLength() && ' ' >= ( c = sBuff[ n ]); ++n )
    {
        if'\x9' == c )
            sBuff.remove( n--, 1 );
    }
    for( n = sBuff.getLength(); n && ' ' >= ( c = sBuff[ --n ]); )
    {
        if'\x9' == c )
            sBuff.remove( n, 1 );
    }
    rText = sBuff.makeStringAndClear();
}

void InsTableBox( SwDoc& rDoc, SwTableNode* pTableNd,
                        SwTableLine* pLine, SwTableBoxFormat* pBoxFrameFormat,
                        SwTableBox* pBox,
                        sal_uInt16 nInsPos, sal_uInt16 nCnt )
{
    OSL_ENSURE( pBox->GetSttNd(), "Box with no start node" );
    SwNodeIndex aIdx( *pBox->GetSttNd(), +1 );
    SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
    if( !pCNd )
        pCNd = SwNodes::GoNext(&aIdx);
    assert(pCNd && "Box with no content node");

    if( pCNd->IsTextNode() )
    {
        if( pCNd->GetpSwAttrSet() )
        {
            SwAttrSet aAttrSet( *pCNd->GetpSwAttrSet() );
            if(pCNd->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT))
            {
                SwFormatAutoFormat format = aAttrSet.Get(RES_PARATR_LIST_AUTOFMT);
                const std::shared_ptr<SfxItemSet>& handle = format.GetStyleHandle();
                aAttrSet.Put(*handle);
            }
            if( pBox->GetSaveNumFormatColor() )
            {
                if( pBox->GetSaveUserColor() )
                    aAttrSet.Put( SvxColorItem( *pBox->GetSaveUserColor(), RES_CHRATR_COLOR ));
                else
                    aAttrSet.ClearItem( RES_CHRATR_COLOR );
            }
            rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
                                    static_cast<SwTextNode*>(pCNd)->GetTextColl(),
                                    &aAttrSet, nInsPos, nCnt );
        }
        else
            rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
                                    static_cast<SwTextNode*>(pCNd)->GetTextColl(),
                                    pCNd->GetpSwAttrSet(), nInsPos, nCnt );
    }
    else
        rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
                rDoc.GetDfltTextFormatColl(), nullptr,
                nInsPos, nCnt );

    sal_Int32 nRowSpan = pBox->getRowSpan();
    if( nRowSpan != 1 )
    {
        SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
        for( sal_uInt16 i = 0; i < nCnt; ++i )
        {
            pBox = rTableBoxes[ i + nInsPos ];
            pBox->setRowSpan( nRowSpan );
        }
    }
}

SwTable::SwTable()
    : SwClient( nullptr ),
    m_pTableNode( nullptr ),
    m_nGraphicsThatResize( 0 ),
    m_nRowsToRepeat( 1 ),
    m_bModifyLocked( false ),
    m_bNewModel( true )
{
    // default value set in the options
    m_eTableChgMode = GetTableChgDefaultMode();
}

SwTable::SwTable( const SwTable& rTable )
    : SwClient( rTable.GetFrameFormat() ),
    m_pTableNode( nullptr ),
    m_eTableChgMode( rTable.m_eTableChgMode ),
    m_nGraphicsThatResize( 0 ),
    m_nRowsToRepeat( rTable.GetRowsToRepeat() ),
    maTableStyleName(rTable.maTableStyleName),
    m_bModifyLocked( false ),
    m_bNewModel( rTable.m_bNewModel )
{
}

void DelBoxNode( SwTableSortBoxes const & rSortCntBoxes )
{
    for (size_t n = 0; n < rSortCntBoxes.size(); ++n)
    {
        rSortCntBoxes[ n ]->m_pStartNode = nullptr;
    }
}

SwTable::~SwTable()
{
    if( m_xRefObj.is() )
    {
        SwDoc& rDoc = GetFrameFormat()->GetDoc();
        if( !rDoc.IsInDtor() )         // then remove from the list
            rDoc.getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_xRefObj.get() );

        m_xRefObj->Closed();
    }

    // the table can be deleted if it's the last client of the FrameFormat
    SwTableFormat* pFormat = GetFrameFormat();
    pFormat->Remove(*this);               // remove

    if( !pFormat->HasWriterListeners() )
        pFormat->GetDoc().DelTableFrameFormat( pFormat );   // and delete

    // Delete the pointers from the SortArray of the boxes. The objects
    // are preserved and are deleted by the lines/boxes arrays dtor.
    // Note: unfortunately not enough, pointers to the StartNode of the
    // section need deletion.
    DelBoxNode(m_TabSortContentBoxes);
    m_TabSortContentBoxes.clear();
}

namespace
{

template<class T>
T lcl_MulDiv64(sal_uInt64 nA, sal_uInt64 nM, sal_uInt64 nD)
{
    assert(nD != 0);
    return nD == 0 ? static_cast<T>(nA*nM) : static_cast<T>((nA*nM)/nD);
}

}

static void FormatInArr( std::vector<SwFormat*>& rFormatArr, SwFormat* pBoxFormat )
{
    std::vector<SwFormat*>::const_iterator it = std::find( rFormatArr.begin(), rFormatArr.end(), pBoxFormat );
    if ( it == rFormatArr.end() )
        rFormatArr.push_back( pBoxFormat );
}

static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
                         const tools::Long nNew, std::vector<SwFormat*>& rFormatArr );

static void lcl_ModifyLines( SwTableLines &rLines, const tools::Long nOld,
                         const tools::Long nNew, std::vector<SwFormat*>& rFormatArr, const bool bCheckSum )
{
    for ( auto &rLine : rLines)
        ::lcl_ModifyBoxes( rLine->GetTabBoxes(), nOld, nNew, rFormatArr );
    if( bCheckSum )
    {
        for(SwFormat* pFormat : rFormatArr)
        {
            const SwTwips nBox = lcl_MulDiv64<SwTwips>(pFormat->GetFrameSize().GetWidth(), nNew, nOld);
            SwFormatFrameSize aNewBox( SwFrameSize::Variable, nBox, 0 );
            pFormat->LockModify();
            pFormat->SetFormatAttr( aNewBox );
            pFormat->UnlockModify();
        }
    }
}

static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
                         const tools::Long nNew, std::vector<SwFormat*>& rFormatArr )
{
    sal_uInt64 nSum = 0; // To avoid rounding errors we summarize all box widths
    sal_uInt64 nOriginalSum = 0; // Sum of original widths
    for ( size_t i = 0; i < rBoxes.size(); ++i )
    {
        SwTableBox &rBox = *rBoxes[i];
        if ( !rBox.GetTabLines().empty() )
        {
            // For SubTables the rounding problem will not be solved :-(
            ::lcl_ModifyLines( rBox.GetTabLines(), nOld, nNew, rFormatArr, false );
        }
        // Adjust the box
        SwFrameFormat *pFormat = rBox.GetFrameFormat();
        sal_uInt64 nBox = pFormat->GetFrameSize().GetWidth();
        nOriginalSum += nBox;
        nBox = lcl_MulDiv64<sal_uInt64>(nBox, nNew, nOld);
        const sal_uInt64 nWishedSum = lcl_MulDiv64<sal_uInt64>(nOriginalSum, nNew, nOld) - nSum;
        if( nWishedSum > 0 )
        {
            if( nBox == nWishedSum )
                FormatInArr( rFormatArr, pFormat );
            else
            {
                nBox = nWishedSum;
                pFormat = rBox.ClaimFrameFormat();
                SwFormatFrameSize aNewBox( SwFrameSize::Variable, static_cast< SwTwips >(nBox), 0 );
                pFormat->LockModify();
                pFormat->SetFormatAttr( aNewBox );
                pFormat->UnlockModify();
            }
        }
        else {
            OSL_FAIL( "Rounding error" );
        }
        nSum += nBox;
    }
}

void SwTable::SwClientNotify(const SwModify&, const SfxHint& rHint)
{
    if(rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
    {
        auto& rAutoFormatUsedHint = static_cast<const sw::AutoFormatUsedHint&>(rHint);
        rAutoFormatUsedHint.CheckNode(GetTableNode());
    }
    else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
    {
        auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
        // catch SSize changes, to adjust the lines/boxes
        const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr;
        if (pChangeHint->m_pOld && pChangeHint->m_pNew
                && (pNewSize = pChangeHint->m_pNew->GetChgSet()->GetItemIfSet(
                        RES_FRM_SIZE,
                        false)))
        {
            pOldSize = &pChangeHint->m_pOld->GetChgSet()->GetFrameSize();
        }
        if (pOldSize && pNewSize && !m_bModifyLocked)
            AdjustWidths(pOldSize->GetWidth(), pNewSize->GetWidth());
    }
    else if (rHint.GetId() == SfxHintId::SwObjectDying)
    {
        auto pDyingHint = static_cast<const sw::ObjectDyingHint*>(&rHint);
        CheckRegistration( *pDyingHint );
    }
    else if (rHint.GetId() == SfxHintId::SwLegacyModify)
    {
        auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
        // catch SSize changes, to adjust the lines/boxes
        const sal_uInt16 nWhich = pLegacy->GetWhich();
        if (nWhich == RES_FRM_SIZE)
        {
            const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr;
            pOldSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pOld);
            pNewSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew);
            if (pOldSize && pNewSize && !m_bModifyLocked)
                AdjustWidths(pOldSize->GetWidth(), pNewSize->GetWidth());
        }
    }
}

void SwTable::AdjustWidths( const tools::Long nOld, const tools::Long nNew )
{
    std::vector<SwFormat*> aFormatArr;
    aFormatArr.reserve( m_aLines[0]->GetTabBoxes().size() );
    ::lcl_ModifyLines( m_aLines, nOld, nNew, aFormatArr, true );
}

static void lcl_RefreshHidden( SwTabCols &rToFill, size_t nPos )
{
    for ( size_t i = 0; i < rToFill.Count(); ++i )
    {
        if ( std::abs(static_cast<tools::Long>(nPos) - rToFill[i]) <= COLFUZZY )
        {
            rToFill.SetHidden( i, false );
            break;
        }
    }
}

static void lcl_SortedTabColInsert( SwTabCols &rToFill, const SwTableBox *pBox,
                   const SwFrameFormat *pTabFormat, const bool bHidden,
                   const bool bRefreshHidden )
{
    const tools::Long nWish = pTabFormat->GetFrameSize().GetWidth();
    OSL_ENSURE(nWish, "weird <= 0 width frmfrm");

    // The value for the left edge of the box is calculated from the
    // widths of the previous boxes.
    tools::Long nPos = 0;
    tools::Long nLeftMin = 0;
    tools::Long nRightMax = 0;
    if (nWish != 0) //fdo#33012 0 width frmfmt
    {
        SwTwips nSum = 0;
        const SwTableBox  *pCur  = pBox;
        const SwTableLine *pLine = pBox->GetUpper();
        const tools::Long nAct  = rToFill.GetRight() - rToFill.GetLeft();  // +1 why?

        while ( pLine )
        {
            const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
            for ( size_t i = 0; i < rBoxes.size(); ++i )
            {
                const SwTwips nWidth = rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
                nSum += nWidth;
                const tools::Long nTmp = lcl_MulDiv64<tools::Long>(nSum, nAct, nWish);

                if (rBoxes[i] != pCur)
                {
                    if ( pLine == pBox->GetUpper() || 0 == nLeftMin )
                        nLeftMin = nTmp - nPos;
                    nPos = nTmp;
                }
                else
                {
                    nSum -= nWidth;
                    if ( 0 == nRightMax )
                        nRightMax = nTmp - nPos;
                    break;
                }
            }
            pCur  = pLine->GetUpper();
            pLine = pCur ? pCur->GetUpper() : nullptr;
        }
    }

    bool bInsert = !bRefreshHidden;
    for ( size_t j = 0; bInsert && (j < rToFill.Count()); ++j )
    {
        tools::Long nCmp = rToFill[j];
        if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
             (nPos <= (nCmp + COLFUZZY)) )
        {
            bInsert = false;        // Already has it.
        }
        else if ( nPos < nCmp )
        {
            bInsert = false;
            rToFill.Insert( nPos, bHidden, j );
        }
    }
    if ( bInsert )
        rToFill.Insert( nPos, bHidden, rToFill.Count() );
    else if ( bRefreshHidden )
        ::lcl_RefreshHidden( rToFill, nPos );

    if ( !bHidden || bRefreshHidden )
        return;

    // calculate minimum/maximum values for the existing entries:
    nLeftMin = nPos - nLeftMin;
    nRightMax = nPos + nRightMax;

    // check if nPos is entry:
    bool bFoundPos = false;
    bool bFoundMax = false;
    for ( size_t j = 0; !(bFoundPos && bFoundMax ) && j < rToFill.Count(); ++j )
    {
        SwTabColsEntry& rEntry = rToFill.GetEntry( j );
        tools::Long nCmp = rToFill[j];

        if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
             (nPos <= (nCmp + COLFUZZY)) )
        {
            // check if nLeftMin is > old minimum for entry nPos:
            const tools::Long nOldMin = rEntry.nMin;
            if ( nLeftMin > nOldMin )
                rEntry.nMin = nLeftMin;
            // check if nRightMin is < old maximum for entry nPos:
            const tools::Long nOldMax = rEntry.nMax;
            if ( nRightMax < nOldMax )
                rEntry.nMax = nRightMax;

            bFoundPos = true;
        }
        else if ( (nRightMax >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
                  (nRightMax <= (nCmp + COLFUZZY)) )
        {
            // check if nPos is > old minimum for entry nRightMax:
            const tools::Long nOldMin = rEntry.nMin;
            if ( nPos > nOldMin )
                rEntry.nMin = nPos;

            bFoundMax = true;
        }
    }
}

static void lcl_ProcessBoxGet( const SwTableBox *pBox, SwTabCols &rToFill,
                        const SwFrameFormat *pTabFormat, bool bRefreshHidden )
{
    if ( !pBox->GetTabLines().empty() )
    {
        const SwTableLines &rLines = pBox->GetTabLines();
        for ( size_t i = 0; i < rLines.size(); ++i )
        {
            const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
            for ( size_t j = 0; j < rBoxes.size(); ++j )
                ::lcl_ProcessBoxGet( rBoxes[j], rToFill, pTabFormat, bRefreshHidden);
        }
    }
    else
        ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, false, bRefreshHidden );
}

static void lcl_ProcessLineGet( const SwTableLine *pLine, SwTabCols &rToFill,
                         const SwFrameFormat *pTabFormat )
{
    for ( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i )
    {
        const SwTableBox *pBox = pLine->GetTabBoxes()[i];
        if ( pBox->GetSttNd() )
            ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, truefalse );
        else
            for ( size_t j = 0; j < pBox->GetTabLines().size(); ++j )
                ::lcl_ProcessLineGet( pBox->GetTabLines()[j], rToFill, pTabFormat );
    }
}

void SwTable::GetTabCols( SwTabCols &rToFill, const SwTableBox *pStart,
              bool bRefreshHidden, bool bCurRowOnly ) const
{
    // Optimization: if bHidden is set, we only update the Hidden Array.
    if ( bRefreshHidden )
    {
        // remove corrections
        for ( size_t i = 0; i < rToFill.Count(); ++i )
        {
            SwTabColsEntry& rEntry = rToFill.GetEntry( i );
            rEntry.nPos -= rToFill.GetLeft();
            rEntry.nMin -= rToFill.GetLeft();
            rEntry.nMax -= rToFill.GetLeft();
        }

        // All are hidden, so add the visible ones.
        for ( size_t i = 0; i < rToFill.Count(); ++i )
            rToFill.SetHidden( i, true );
    }
    else
    {
        rToFill.Remove( 0, rToFill.Count() );
    }

    // Insertion cases:
    // 1. All boxes which are inferior to Line which is superior to the Start,
    //    as well as their inferior boxes if present.
    // 2. Starting from the Line, the superior box plus its neighbours; but no inferiors.
    // 3. Apply 2. to the Line superior to the chain of boxes,
    //    until the Line's superior is not a box but the table.
    // Only those boxes are inserted that don't contain further rows. The insertion
    // function takes care to avoid duplicates. In order to achieve this, we work
    // with some degree of fuzzyness (to avoid rounding errors).
    // Only the left edge of the boxes are inserted.
    // Finally, the first entry is removed again, because it's already
    // covered by the border.
    // 4. Scan the table again and insert _all_ boxes, this time as hidden.

    const SwFrameFormat *pTabFormat = GetFrameFormat();

    // 1.
    const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();

    for ( size_t i = 0; i < rBoxes.size(); ++i )
        ::lcl_ProcessBoxGet( rBoxes[i], rToFill, pTabFormat, bRefreshHidden );

    // 2. and 3.
    const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
                                pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
    while ( pLine )
    {
        const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
        for ( size_t k = 0; k < rBoxes2.size(); ++k )
            ::lcl_SortedTabColInsert( rToFill, rBoxes2[k],
                                      pTabFormat, false, bRefreshHidden );
        pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
    }

    if ( !bRefreshHidden )
    {
        // 4.
        if ( !bCurRowOnly )
        {
            for ( size_t i = 0; i < m_aLines.size(); ++i )
                ::lcl_ProcessLineGet( m_aLines[i], rToFill, pTabFormat );
        }

        rToFill.Remove( 0 );
    }

    // Now the coordinates are relative to the left table border - i.e.
    // relative to SwTabCols.nLeft. However, they are expected
    // relative to the left document border, i.e. SwTabCols.nLeftMin.
    // So all values need to be extended by nLeft.
    for ( size_t i = 0; i < rToFill.Count(); ++i )
    {
        SwTabColsEntry& rEntry = rToFill.GetEntry( i );
        rEntry.nPos += rToFill.GetLeft();
        rEntry.nMin += rToFill.GetLeft();
        rEntry.nMax += rToFill.GetLeft();
    }
}

// Structure for parameter passing
struct Parm
{
    const SwTabCols &rNew;
    const SwTabCols &rOld;
    tools::Long nNewWish,
         nOldWish;
    std::deque<SwTableBox*> aBoxArr;
    SwShareBoxFormats aShareFormats;

    Parm( const SwTabCols &rN, const SwTabCols &rO )
        : rNew( rN ), rOld( rO ), nNewWish(0), nOldWish(0)
    {}
};

static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm );

static void lcl_ProcessLine( SwTableLine *pLine, Parm &rParm )
{
    SwTableBoxes &rBoxes = pLine->GetTabBoxes();
    for ( size_t i = rBoxes.size(); i > 0; )
    {
        --i;
        ::lcl_ProcessBoxSet( rBoxes[i], rParm );
    }
}

static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm )
{
    if ( !pBox->GetTabLines().empty() )
    {
        SwTableLines &rLines = pBox->GetTabLines();
        for ( size_t i = rLines.size(); i > 0; )
        {
            --i;
            lcl_ProcessLine( rLines[i], rParm );
        }
    }
    else
    {
        // Search the old TabCols for the current position (calculate from
        // left and right edge). Adjust the box if the values differ from
        // the new TabCols. If the adjusted edge has no neighbour we also
        // adjust all superior boxes.

        const tools::Long nOldAct = rParm.rOld.GetRight() -
                             rParm.rOld.GetLeft(); // +1 why?

        // The value for the left edge of the box is calculated from the
        // widths of the previous boxes plus the left edge.
        tools::Long nLeft = rParm.rOld.GetLeft();
        const  SwTableBox  *pCur  = pBox;
        const  SwTableLine *pLine = pBox->GetUpper();

        while ( pLine )
        {
            const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
            for ( size_t i = 0; (i < rBoxes.size()) && (rBoxes[i] != pCur); ++i)
            {
                nLeft += lcl_MulDiv64<tools::Long>(
                    rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(),
                    nOldAct, rParm.nOldWish);
            }
            pCur  = pLine->GetUpper();
            pLine = pCur ? pCur->GetUpper() : nullptr;
        }
        tools::Long nLeftDiff = 0;
        tools::Long nRightDiff = 0;
        if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this.
        {
            // Right edge is left edge plus width.
            const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
                pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
                nOldAct, rParm.nOldWish);
            const tools::Long nRight = nLeft + nWidth;
            size_t nLeftPos  = 0;
            size_t nRightPos = 0;
            bool bFoundLeftPos = false;
            bool bFoundRightPos = false;
            for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
            {
                if ( nLeft >= (rParm.rOld[i] - COLFUZZY) &&
                     nLeft <= (rParm.rOld[i] + COLFUZZY) )
                {
                    nLeftPos = i;
                    bFoundLeftPos = true;
                }
                else if ( nRight >= (rParm.rOld[i] - COLFUZZY) &&
                          nRight <= (rParm.rOld[i] + COLFUZZY) )
                {
                    nRightPos = i;
                    bFoundRightPos = true;
                }
            }
            nLeftDiff = bFoundLeftPos ?
                rParm.rOld[nLeftPos] - rParm.rNew[nLeftPos] : 0;
            nRightDiff= bFoundRightPos ?
                rParm.rNew[nRightPos] - rParm.rOld[nRightPos] : 0;
        }
        else    // The first box.
        {
            nLeftDiff = rParm.rOld.GetLeft() - rParm.rNew.GetLeft();
            if ( rParm.rOld.Count() )
            {
                // Calculate the difference to the edge touching the first box.
                const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
                    pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
                    nOldAct, rParm.nOldWish);
                const tools::Long nTmp = nWidth + rParm.rOld.GetLeft();
                for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
                {
                    if ( nTmp >= (rParm.rOld[i] - COLFUZZY) &&
                         nTmp <= (rParm.rOld[i] + COLFUZZY) )
                    {
                        nRightDiff = rParm.rNew[i] - rParm.rOld[i];
                        break;
                    }
                }
            }
        }

        if( pBox->getRowSpan() == 1 )
        {
            const sal_uInt16 nPos = pBox->GetUpper()->GetBoxPos( pBox );
            SwTableBoxes& rTableBoxes = pBox->GetUpper()->GetTabBoxes();
            if( nPos && rTableBoxes[ nPos - 1 ]->getRowSpan() != 1 )
                nLeftDiff = 0;
            if( nPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) &&
                rTableBoxes[ nPos + 1 ]->getRowSpan() != 1 )
                nRightDiff = 0;
        }
        else
            nLeftDiff = nRightDiff = 0;

        if ( nLeftDiff || nRightDiff )
        {
            // The difference is the actual difference amount. For stretched
            // tables, it does not make sense to adjust the attributes of the
            // boxes by this amount. The difference amount needs to be converted
            // accordingly.
            tools::Long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why?
            nLeftDiff *= rParm.nNewWish;
            nLeftDiff /= nTmp;
            nRightDiff *= rParm.nNewWish;
            nRightDiff /= nTmp;
            tools::Long nDiff = nLeftDiff + nRightDiff;

            // Adjust the box and all superiors by the difference amount.
            while ( pBox )
            {
                SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
                aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );
                if ( aFormatFrameSize.GetWidth() < 0 )
                    aFormatFrameSize.SetWidth( -aFormatFrameSize.GetWidth() );
                rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );

                // The outer cells of the last row are responsible to adjust a surrounding cell.
                // Last line check:
                if ( pBox->GetUpper()->GetUpper() &&
                     pBox->GetUpper() != pBox->GetUpper()->GetUpper()->GetTabLines().back())
                {
                    pBox = nullptr;
                }
                else
                {
                    // Middle cell check:
                    if ( pBox != pBox->GetUpper()->GetTabBoxes().front() )
                        nDiff = nRightDiff;

                    if ( pBox != pBox->GetUpper()->GetTabBoxes().back() )
                        nDiff -= nRightDiff;

                    pBox = nDiff ? pBox->GetUpper()->GetUpper() : nullptr;
                }
            }
        }
    }
}

static void lcl_ProcessBoxPtr( SwTableBox *pBox, std::deque<SwTableBox*> &rBoxArr,
                           bool bBefore )
{
    if ( !pBox->GetTabLines().empty() )
    {
        const SwTableLines &rLines = pBox->GetTabLines();
        for ( size_t i = 0; i < rLines.size(); ++i )
        {
            const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
            for ( size_t j = 0; j < rBoxes.size(); ++j )
                ::lcl_ProcessBoxPtr( rBoxes[j], rBoxArr, bBefore );
        }
    }
    else if ( bBefore )
        rBoxArr.push_front( pBox );
    else
        rBoxArr.push_back( pBox );
}

static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm );

static void lcl_AdjustLines( SwTableLines &rLines, const tools::Long nDiff, Parm &rParm )
{
    for ( size_t i = 0; i < rLines.size(); ++i )
    {
        SwTableBox *pBox = rLines[i]->GetTabBoxes()
                                [rLines[i]->GetTabBoxes().size()-1];
        lcl_AdjustBox( pBox, nDiff, rParm );
    }
}

static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm )
{
    if ( !pBox->GetTabLines().empty() )
        ::lcl_AdjustLines( pBox->GetTabLines(), nDiff, rParm );

    // Adjust the size of the box.
    SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
    aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );

    rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
}

void SwTable::SetTabCols( const SwTabCols &rNew, const SwTabCols &rOld,
                          const SwTableBox *pStart, bool bCurRowOnly )
{
    CHECK_TABLE( *this )

    SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());    // delete HTML-Layout

    // FME: Made rOld const. The caller is responsible for passing correct
    // values of rOld. Therefore we do not have to call GetTabCols anymore:
    //GetTabCols( rOld, pStart );

    Parm aParm( rNew, rOld );

    OSL_ENSURE( rOld.Count() == rNew.Count(), "Number of columns changed.");

    // Convert the edges. We need to adjust the size of the table and some boxes.
    // For the size adjustment, we must not make use of the Modify, since that'd
    // adjust all boxes, which we really don't want.
    SwFrameFormat *pFormat = GetFrameFormat();
    aParm.nOldWish = aParm.nNewWish = pFormat->GetFrameSize().GetWidth();
    if ( (rOld.GetLeft() != rNew.GetLeft()) ||
         (rOld.GetRight()!= rNew.GetRight()) )
    {
        LockModify();
        {
            SvxLRSpaceItem aLR( pFormat->GetLRSpace() );
            SvxShadowItem aSh( pFormat->GetShadow() );

            SwTwips nShRight = aSh.CalcShadowSpace( SvxShadowItemSide::RIGHT );
            SwTwips nShLeft = aSh.CalcShadowSpace( SvxShadowItemSide::LEFT );

            aLR.SetLeft(SvxIndentValue::twips(rNew.GetLeft() - nShLeft));
            aLR.SetRight(SvxIndentValue::twips(rNew.GetRightMax() - rNew.GetRight() - nShRight));
            pFormat->SetFormatAttr( aLR );

            // The alignment of the table needs to be adjusted accordingly.
            // This is done by preserving the exact positions that have been
            // set by the user.
            SwFormatHoriOrient aOri( pFormat->GetHoriOrient() );
            if( text::HoriOrientation::NONE != aOri.GetHoriOrient() &&
                text::HoriOrientation::CENTER != aOri.GetHoriOrient() )
            {
                const bool bLeftDist = rNew.GetLeft() != nShLeft;
                const bool bRightDist = rNew.GetRight() + nShRight != rNew.GetRightMax();
                if(!bLeftDist && !bRightDist)
                    aOri.SetHoriOrient( text::HoriOrientation::FULL );
                else if(!bRightDist && rNew.GetLeft() > nShLeft )
                    aOri.SetHoriOrient( text::HoriOrientation::RIGHT );
                else if(!bLeftDist && rNew.GetRight() + nShRight < rNew.GetRightMax())
                    aOri.SetHoriOrient( text::HoriOrientation::LEFT );
                else
                {
                    // if an automatic table hasn't (really) changed size, then leave it as auto.
                    const tools::Long nOldWidth = rOld.GetRight() - rOld.GetLeft();
                    const tools::Long nNewWidth = rNew.GetRight() - rNew.GetLeft();
                    if (aOri.GetHoriOrient() != text::HoriOrientation::FULL
                        || std::abs(nOldWidth - nNewWidth) > COLFUZZY)
                    {
                        aOri.SetHoriOrient(text::HoriOrientation::LEFT_AND_WIDTH);
                    }
                }
            }
            pFormat->SetFormatAttr( aOri );
        }
        const tools::Long nAct = rOld.GetRight() - rOld.GetLeft(); // +1 why?
        tools::Long nTabDiff = 0;

        if ( rOld.GetLeft() != rNew.GetLeft() )
        {
            nTabDiff = rOld.GetLeft() - rNew.GetLeft();
            nTabDiff *= aParm.nOldWish;
            nTabDiff /= nAct;
        }
        if ( rOld.GetRight() != rNew.GetRight() )
        {
            tools::Long nDiff = rNew.GetRight() - rOld.GetRight();
            nDiff *= aParm.nOldWish;
            nDiff /= nAct;
            nTabDiff += nDiff;
            if( !IsNewModel() )
                ::lcl_AdjustLines( GetTabLines(), nDiff, aParm );
        }

        // Adjust the size of the table, watch out for stretched tables.
        if ( nTabDiff )
        {
            aParm.nNewWish += nTabDiff;
            if ( aParm.nNewWish < 0 )
                aParm.nNewWish = USHRT_MAX; // Oops! Have to roll back.
            SwFormatFrameSize aSz( pFormat->GetFrameSize() );
            if ( aSz.GetWidth() != aParm.nNewWish )
            {
                aSz.SetWidth( aParm.nNewWish );
                aSz.SetWidthPercent( 0 );
                pFormat->SetFormatAttr( aSz );
            }
        }
        UnlockModify();
    }

    if( IsNewModel() )
        NewSetTabCols( aParm, rNew, rOld, pStart, bCurRowOnly );
    else
    {
        if ( bCurRowOnly )
        {
            // To adjust the current row, we need to process all its boxes,
            // similar to the filling of the TabCols (see GetTabCols()).
            // Unfortunately we again have to take care to adjust the boxes
            // from back to front, respectively from outer to inner.
            // The best way to achieve this is probably to track the boxes
            // in a PtrArray.
            const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();
            for ( size_t i = 0; i < rBoxes.size(); ++i )
                ::lcl_ProcessBoxPtr( rBoxes[i], aParm.aBoxArr, false );

            const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
                                    pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
            const SwTableBox  *pExcl = pStart->GetUpper()->GetUpper();
            while ( pLine )
            {
                const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
                bool bBefore = true;
                for ( size_t i = 0; i < rBoxes2.size(); ++i )
                {
                    if ( rBoxes2[i] != pExcl )
                        ::lcl_ProcessBoxPtr( rBoxes2[i], aParm.aBoxArr, bBefore );
                    else
                        bBefore = false;
                }
                pExcl = pLine->GetUpper();
                pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
            }
            // After we've inserted a bunch of boxes (hopefully all and in
            // correct order), we just need to process them in reverse order.
            for ( int j = aParm.aBoxArr.size()-1; j >= 0; --j )
            {
                SwTableBox *pBox = aParm.aBoxArr[j];
                ::lcl_ProcessBoxSet( pBox, aParm );
            }
        }
        else
        {
            // Adjusting the entire table is 'easy'. All boxes without lines are
            // adjusted, as are their superiors. Of course we need to process
            // in reverse order to prevent fooling ourselves!
            SwTableLines &rLines = GetTabLines();
            for ( size_t i = rLines.size(); i > 0; )
            {
                --i;
                ::lcl_ProcessLine( rLines[i], aParm );
            }
        }
    }

#ifdef DBG_UTIL
    CheckBoxWidth(GetTabLines(), *GetFrameFormat());
#endif
}

typedef std::pair<sal_uInt16, sal_uInt16> ColChange;
typedef std::list< ColChange > ChangeList;

static void lcl_AdjustWidthsInLine( SwTableLine* pLine, ChangeList& rOldNew,
    Parm& rParm, sal_uInt16 nColFuzzy )
{
    ChangeList::iterator pCurr = rOldNew.begin();
    if( pCurr == rOldNew.end() )
        return;
    const size_t nCount = pLine->GetTabBoxes().size();
    SwTwips nBorder = 0;
    SwTwips nRest = 0;
    for( size_t i = 0; i < nCount; ++i )
    {
        SwTableBox* pBox = pLine->GetTabBoxes()[i];
        SwTwips nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
        SwTwips nNewWidth = nWidth - nRest;
        nRest = 0;
        nBorder += nWidth;
        if( pCurr != rOldNew.end() && nBorder + nColFuzzy >= pCurr->first )
        {
            nBorder -= nColFuzzy;
            while( pCurr != rOldNew.end() && nBorder > pCurr->first )
                ++pCurr;
            if( pCurr != rOldNew.end() )
            {
                nBorder += nColFuzzy;
                if( nBorder + nColFuzzy >= pCurr->first )
                {
                    if( pCurr->second == pCurr->first )
                        nRest = 0;
                    else
                        nRest = pCurr->second - nBorder;
                    nNewWidth += nRest;
                    ++pCurr;
                }
            }
        }
        if( nNewWidth != nWidth )
        {
            if( nNewWidth < 0 )
            {
                nRest += 1 - nNewWidth;
                nNewWidth = 1;
            }
            SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
            aFormatFrameSize.SetWidth( nNewWidth );
            rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
        }
    }
}

static void lcl_CalcNewWidths( std::vector<sal_uInt16> &rSpanPos, ChangeList& rChanges,
    SwTableLine* pLine, tools::Long nWish, tools::Long nWidth, bool bTop )
{
    if( rChanges.empty() )
    {
        rSpanPos.clear();
        return;
    }
    if( rSpanPos.empty() )
    {
        rChanges.clear();
        return;
    }
    std::vector<sal_uInt16> aNewSpanPos;
    ChangeList::iterator pCurr = rChanges.begin();
    ChangeList aNewChanges { *pCurr }; // Nullposition
    std::vector<sal_uInt16>::iterator pSpan = rSpanPos.begin();
    sal_uInt16 nCurr = 0;
    SwTwips nOrgSum = 0;
    bool bRowSpan = false;
    sal_uInt16 nRowSpanCount = 0;
    const size_t nCount = pLine->GetTabBoxes().size();
    for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
    {
        SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
        SwTwips nCurrWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
        const sal_Int32 nRowSpan = pBox->getRowSpan();
        const bool bCurrRowSpan = bTop ? nRowSpan < 0 :
            ( nRowSpan > 1 || nRowSpan < -1 );
        if( bRowSpan || bCurrRowSpan )
            aNewSpanPos.push_back( nRowSpanCount );
        bRowSpan = bCurrRowSpan;
        nOrgSum += nCurrWidth;
        const sal_uInt16 nPos = lcl_MulDiv64<sal_uInt16>(
            lcl_MulDiv64<sal_uInt64>(nOrgSum, nWidth, nWish),
            nWish, nWidth);
        while( pCurr != rChanges.end() && pCurr->first < nPos )
        {
            ++nCurr;
            ++pCurr;
        }
        bool bNew = true;
        if( pCurr != rChanges.end() && pCurr->first <= nPos &&
            pCurr->first != pCurr->second )
        {
            pSpan = std::find_if(pSpan, rSpanPos.end(),
                [nCurr](const sal_uInt16 nSpan) { return nSpan >= nCurr; });
            if( pSpan != rSpanPos.end() && *pSpan == nCurr )
            {
                aNewChanges.push_back( *pCurr );
                ++nRowSpanCount;
                bNew = false;
            }
        }
        if( bNew )
        {
            ColChange aTmp( nPos, nPos );
            aNewChanges.push_back( aTmp );
            ++nRowSpanCount;
        }
    }

    pCurr = aNewChanges.begin();
    ChangeList::iterator pLast = pCurr;
    ChangeList::iterator pLeftMove = pCurr;
    while( pCurr != aNewChanges.end() )
    {
        if( pLeftMove == pCurr )
        {
            while( ++pLeftMove != aNewChanges.end() && pLeftMove->first <= pLeftMove->second )
                ;
        }
        if( pCurr->second == pCurr->first )
        {
            if( pLeftMove != aNewChanges.end() && pCurr->second > pLeftMove->second )
            {
                if( pLeftMove->first == pLast->first )
                    pCurr->second = pLeftMove->second;
                else
                {
                    pCurr->second = lcl_MulDiv64<sal_uInt16>(
                        pCurr->first - pLast->first,
                        pLeftMove->second - pLast->second,
                        pLeftMove->first - pLast->first) + pLast->second;
                }
            }
            pLast = pCurr;
            ++pCurr;
        }
        else if( pCurr->second > pCurr->first )
        {
            pLast = pCurr;
            ++pCurr;
            ChangeList::iterator pNext = pCurr;
            while( pNext != pLeftMove && pNext->second == pNext->first &&
                pNext->second < pLast->second )
                ++pNext;
            while( pCurr != pNext )
            {
                if( pNext == aNewChanges.end() || pNext->first == pLast->first )
                    pCurr->second = pLast->second;
                else
                {
                    pCurr->second = lcl_MulDiv64<sal_uInt16>(
                        pCurr->first - pLast->first,
                        pNext->second - pLast->second,
                        pNext->first - pLast->first) + pLast->second;
                }
                ++pCurr;
            }
            pLast = pCurr;
        }
        else
        {
            pLast = pCurr;
            ++pCurr;
        }
    }

    rChanges.swap(aNewChanges);
    rSpanPos.swap(aNewSpanPos);
}

void SwTable::NewSetTabCols( Parm &rParm, const SwTabCols &rNew,
    const SwTabCols &rOld, const SwTableBox *pStart, bool bCurRowOnly )
{
#if OSL_DEBUG_LEVEL > 1
    static int nCallCount = 0;
    ++nCallCount;
#endif
    // First step: evaluate which lines have been moved/which widths changed
    ChangeList aOldNew;
    const tools::Long nNewWidth = rParm.rNew.GetRight() - rParm.rNew.GetLeft();
    const tools::Long nOldWidth = rParm.rOld.GetRight() - rParm.rOld.GetLeft();
    if( nNewWidth < 1 || nOldWidth < 1 )
        return;
    for( size_t i = 0; i <= rOld.Count(); ++i )
    {
        tools::Long nNewPos;
        tools::Long nOldPos;
        if( i == rOld.Count() )
        {
            nOldPos = rParm.rOld.GetRight() - rParm.rOld.GetLeft();
            nNewPos = rParm.rNew.GetRight() - rParm.rNew.GetLeft();
        }
        else
        {
            nOldPos = rOld[i] - rParm.rOld.GetLeft();
            nNewPos = rNew[i] - rParm.rNew.GetLeft();
        }
        nNewPos = lcl_MulDiv64<tools::Long>(nNewPos, rParm.nNewWish, nNewWidth);
        nOldPos = lcl_MulDiv64<tools::Long>(nOldPos, rParm.nOldWish, nOldWidth);
        if( nOldPos != nNewPos && nNewPos > 0 && nOldPos > 0 )
        {
            ColChange aChg( o3tl::narrowing<sal_uInt16>(nOldPos), o3tl::narrowing<sal_uInt16>(nNewPos) );
            aOldNew.push_back( aChg );
        }
    }
    // Finished first step
    int nCount = aOldNew.size();
    if( !nCount )
        return// no change, nothing to do
    SwTableLines &rLines = GetTabLines();
    if( bCurRowOnly )
    {
        const SwTableLine* pCurrLine = pStart->GetUpper();
        sal_uInt16 nCurr = rLines.GetPos( pCurrLine );
        if( nCurr >= USHRT_MAX )
            return;

        ColChange aChg( 0, 0 );
        aOldNew.push_front( aChg );
        std::vector<sal_uInt16> aRowSpanPos;
        if (nCurr > 0)
        {
            ChangeList aCopy;
            sal_uInt16 nPos = 0;
            forconst auto& rCop : aOldNew )
            {
                aCopy.push_back( rCop );
                aRowSpanPos.push_back( nPos++ );
            }
            lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr],
                rParm.nOldWish, nOldWidth, true );
            sal_uInt16 j = nCurr;
            while (!aRowSpanPos.empty() && j > 0)
            {
                j = o3tl::sanitizing_dec(j);
                lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[j],
                    rParm.nOldWish, nOldWidth, true );
                lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
            }
            aRowSpanPos.clear();
        }
        if( nCurr+1 < o3tl::narrowing<sal_uInt16>(rLines.size()) )
        {
            ChangeList aCopy;
            sal_uInt16 nPos = 0;
            forconst auto& rCop : aOldNew )
            {
                aCopy.push_back( rCop );
                aRowSpanPos.push_back( nPos++ );
            }
            lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr],
                rParm.nOldWish, nOldWidth, false );
            bool bGoOn = !aRowSpanPos.empty();
            sal_uInt16 j = nCurr;
            while( bGoOn )
            {
                lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[++j],
                    rParm.nOldWish, nOldWidth, false );
                lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
                bGoOn = !aRowSpanPos.empty() && j+1 < o3tl::narrowing<sal_uInt16>(rLines.size());
            }
        }
        ::lcl_AdjustWidthsInLine( rLines[nCurr], aOldNew, rParm, COLFUZZY );
    }
    else
    {
        for( size_t i = 0; i < rLines.size(); ++i )
            ::lcl_AdjustWidthsInLine( rLines[i], aOldNew, rParm, COLFUZZY );
    }
    CHECK_TABLE( *this )
}

// return the pointer of the box specified.
static bool lcl_IsValidRowName( std::u16string_view rStr )
{
    bool bIsValid = true;
    size_t nLen = rStr.size();
    for( size_t i = 0;  i < nLen && bIsValid; ++i )
    {
        const sal_Unicode cChar = rStr[i];
        if (cChar < '0' || cChar > '9')
            bIsValid = false;
    }
    return bIsValid;
}

// #i80314#
// add 3rd parameter and its handling
sal_uInt16 SwTable::GetBoxNum( OUString& rStr, bool bFirstPart,
                            const bool bPerformValidCheck )
{
    sal_uInt16 nRet = 0;
    if( bFirstPart )   // true == column; false == row
    {
        sal_Int32 nPos = 0;
        // the first one uses letters for addressing!
        bool bFirst = true;
        sal_uInt32 num = 0;
        bool overflow = false;
        while (nPos<rStr.getLength())
        {
            sal_Unicode cChar = rStr[nPos];
            if ((cChar<'A' || cChar>'Z') && (cChar<'a' || cChar>'z'))
                break;
            cChar -= 'A';
            if( cChar >= 26 )
                cChar -= 'a' - '[';
            if( bFirst )
                bFirst = false;
            else
                ++num;
            num = num * 52 + cChar;
            if (num > SAL_MAX_UINT16) {
                overflow = true;
            }
            ++nPos;
        }
        nRet = overflow ? SAL_MAX_UINT16 : num;
        rStr = rStr.copy( nPos );      // Remove char from String
    }
    else
    {
        const sal_Int32 nPos = rStr.indexOf( "." );
        if ( nPos<0 )
        {
            nRet = 0;
            if ( !bPerformValidCheck || lcl_IsValidRowName( rStr ) )
            {
                nRet = o3tl::narrowing<sal_uInt16>(rStr.toInt32());
            }
            rStr.clear();
        }
        else
        {
            nRet = 0;
            const std::u16string_view aText( rStr.subView( 0, nPos ) );
            if ( !bPerformValidCheck || lcl_IsValidRowName( aText ) )
            {
                nRet = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(aText));
            }
            rStr = rStr.copy( nPos+1 );
        }
    }
    return nRet;
}

// #i80314#
// add 2nd parameter and its handling
const SwTableBox* SwTable::GetTableBox( const OUString& rName,
                                      const bool bPerformValidCheck ) const
{
    const SwTableBox* pBox = nullptr;
    const SwTableLine* pLine;
    const SwTableLines* pLines;

    sal_uInt16 nLine, nBox;
    OUString aNm( rName );
    while( !aNm.isEmpty() )
    {
        nBox = SwTable::GetBoxNum( aNm, nullptr == pBox, bPerformValidCheck );
        // first box ?
        if( !pBox )
            pLines = &GetTabLines();
        else
        {
            pLines = &pBox->GetTabLines();
            if( nBox )
                --nBox;
        }

        nLine = SwTable::GetBoxNum( aNm, false, bPerformValidCheck );

        // determine line
        if( !nLine || nLine > pLines->size() )
            return nullptr;
        pLine = (*pLines)[ nLine-1 ];

        // determine box
        const SwTableBoxes* pBoxes = &pLine->GetTabBoxes();
        if( nBox >= pBoxes->size() )
            return nullptr;
        pBox = (*pBoxes)[ nBox ];
    }

    // check if the box found has any contents
    if( pBox && !pBox->GetSttNd() )
    {
        OSL_FAIL( "Box without content, looking for the next one!" );
        // "drop this" until the first box
        while( !pBox->GetTabLines().empty() )
            pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
    }
    return pBox;
}

SwTableBox* SwTable::GetTableBox( SwNodeOffset nSttIdx )
{
    // For optimizations, don't always process the entire SortArray.
    // Converting text to table, tries certain conditions
    // to ask for a table box of a table that is not yet having a format
    if(!GetFrameFormat())
        return nullptr;
    SwTableBox* pRet = nullptr;
    SwNodes& rNds = GetFrameFormat()->GetDoc().GetNodes();
    SwNodeOffset nIndex = nSttIdx + 1;
    SwContentNode* pCNd = nullptr;
    SwTableNode* pTableNd = nullptr;

    while ( nIndex < rNds.Count() )
    {
        pTableNd = rNds[ nIndex ]->GetTableNode();
        if ( pTableNd )
            break;

        pCNd = rNds[ nIndex ]->GetContentNode();
        if ( pCNd )
            break;

        ++nIndex;
    }

    if ( pCNd || pTableNd )
    {
        sw::BroadcastingModify* pModify = pCNd;
        // #144862# Better handling of table in table
        if ( pTableNd && pTableNd->GetTable().GetFrameFormat() )
            pModify = pTableNd->GetTable().GetFrameFormat();

        SwFrame* pFrame = pModify ? SwIterator<SwFrame,sw::BroadcastingModify>(*pModify).First() : nullptr;
        while ( pFrame && !pFrame->IsCellFrame() )
            pFrame = pFrame->GetUpper();
        if ( pFrame )
            pRet = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
    }

    // In case the layout doesn't exist yet or anything else goes wrong.
    if ( !pRet )
    {
        for (size_t n = m_TabSortContentBoxes.size(); n; )
        {
            if (m_TabSortContentBoxes[ --n ]->GetSttIdx() == nSttIdx)
            {
                return m_TabSortContentBoxes[ n ];
            }
        }
    }
    return pRet;
}

bool SwTable::IsTableComplex() const
{
    // Returns true for complex tables, i.e. tables that contain nestings,
    // like containing boxes not part of the first line, e.g. results of
    // splits/merges which lead to more complex structures.
    for (size_t n = 0; n < m_TabSortContentBoxes.size(); ++n)
    {
        if (m_TabSortContentBoxes[ n ]->GetUpper()->GetUpper())
        {
            return true;
        }
    }
    return false;
}

SwTableLine::SwTableLine( SwTableLineFormat *pFormat, sal_uInt16 nBoxes,
                            SwTableBox *pUp )
    : SwClient( pFormat )
    , m_pUpper( pUp )
    , m_eRedlineType( RedlineType::None )
{
    m_aBoxes.reserve( nBoxes );
}

SwTableLine::~SwTableLine()
{
    for (size_t i = 0; i < m_aBoxes.size(); ++i)
    {
        delete m_aBoxes[i];
    }
    // the TabelleLine can be deleted if it's the last client of the FrameFormat
    sw::BroadcastingModify* pMod = GetFrameFormat();
    pMod->Remove(*this);               // remove,
    if( !pMod->HasWriterListeners() )
        delete pMod;    // and delete
}

SwTableLineFormat* SwTableLine::ClaimFrameFormat()
{
    // This method makes sure that this object is an exclusive SwTableLine client
    // of an SwTableLineFormat object
    // If other SwTableLine objects currently listen to the same SwTableLineFormat as
    // this one, something needs to be done
    SwTableLineFormat *pRet = GetFrameFormat();
    SwIterator<SwTableLine,SwFormat> aIter( *pRet );
    for( SwTableLine* pLast = aIter.First(); pLast; pLast = aIter.Next() )
    {
        if ( pLast != this )
        {
            // found another SwTableLine that is a client of the current Format
            // create a new Format as a copy and use it for this object
            SwTableLineFormat *pNewFormat = pRet->GetDoc().MakeTableLineFormat();
            *pNewFormat = *pRet;

            // register SwRowFrames that know me as clients at the new Format
            SwIterator<SwRowFrame,SwFormat> aFrameIter( *pRet );
            for( SwRowFrame* pFrame = aFrameIter.First(); pFrame; pFrame = aFrameIter.Next() )
                if( pFrame->GetTabLine() == this )
                    pFrame->RegisterToFormat( *pNewFormat );

            // register myself
            pNewFormat->Add(*this);
            pRet = pNewFormat;
            break;
        }
    }

    return pRet;
}

void SwTableLine::ChgFrameFormat(SwTableLineFormat* pNewFormat)
{
    auto pOld = GetFrameFormat();
    pOld->CallSwClientNotify(sw::TableLineFormatChanged(*pNewFormat, *this));
    // Now, re-register self.
    pNewFormat->Add(*this);
    if(!pOld->HasWriterListeners())
        delete pOld;
}

SwTwips SwTableLine::GetTableLineHeight( bool& bLayoutAvailable ) const
{
    SwTwips nRet = 0;
    bLayoutAvailable = false;
    SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() );
    // A row could appear several times in headers/footers so only one chain of master/follow tables
    // will be accepted...
    const SwTabFrame* pChain = nullptr; // My chain
    for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
    {
        if (pLast->GetTabLine() != this)
            continue;

        const SwTabFrame* pTab = pLast->FindTabFrame();
        if (!pTab)
            continue;

        bLayoutAvailable = ( pTab->IsVertical() ) ?
                           ( 0 < pTab->getFrameArea().Height() ) :
                           ( 0 < pTab->getFrameArea().Width() );

        // The first one defines the chain, if a chain is defined, only members of the chain
        // will be added.
        if (!pChain || pChain->IsAnFollow( pTab ) || pTab->IsAnFollow(pChain))
        {
            pChain = pTab; // defines my chain (even it is already)
            if( pTab->IsVertical() )
                nRet += pLast->getFrameArea().Width();
            else
                nRet += pLast->getFrameArea().Height();
            // Optimization, if there are no master/follows in my chain, nothing more to add
            if( !pTab->HasFollow() && !pTab->IsFollow() )
                break;
            // This is not an optimization, this is necessary to avoid double additions of
            // repeating rows
            if( pTab->IsInHeadline(*pLast) )
                break;
        }
    }
    return nRet;
}

bool SwTableLine::IsEmpty() const
{
    for (size_t i = 0; i < m_aBoxes.size(); ++i)
    {
        if ( !m_aBoxes[i]->IsEmpty() )
            return false;
    }
    return true;
}

bool SwTable::IsEmpty() const
{
    for (size_t i = 0; i < m_aLines.size(); ++i)
    {
        if ( !m_aLines[i]->IsEmpty() )
            return false;
    }
    return true;
}

bool SwTable::HasDeletedRowOrCell() const
{
    const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
    if ( aRedlineTable.empty() )
        return false;

    SwRedlineTable::size_type nRedlinePos = 0;
    for (size_t i = 0; i < m_aLines.size(); ++i)
    {
        // has a deleted row
        if ( m_aLines[i]->IsDeleted(nRedlinePos) )
            return true;

        // has a deleted cell in the not deleted row
        SwTableBoxes& rBoxes = m_aLines[i]->GetTabBoxes();
        for( size_t j = 0; j < rBoxes.size(); ++j )
        {
            if ( RedlineType::Delete == rBoxes[j]->GetRedlineType() )
                return true;
        }
    }
    return false;
}

bool SwTable::IsDeleted() const
{
    const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
    if ( aRedlineTable.empty() )
        return false;

    SwRedlineTable::size_type nRedlinePos = 0;
    for (size_t i = 0; i < m_aLines.size(); ++i)
    {
        if ( !m_aLines[i]->IsDeleted(nRedlinePos) )
            return false;
    }
    return true;
}

void SwTable::GatherFormulas(std::vector<SwTableBoxFormula*>& rvFormulas)
{
    GatherFormulas(GetFrameFormat()->GetDoc(), rvFormulas);
}

void SwTable::GatherFormulas(SwDoc& rDoc, std::vector<SwTableBoxFormula*>& rvFormulas)
{
    rvFormulas.clear();
    sw::TableFrameFormats* pTableFrameFormats = rDoc.GetTableFrameFormats();
    for(SwTableFormat* pFormat : *pTableFrameFormats)
    {
        SwTable* pTable = FindTable(pFormat);
        if (!pTable)
            continue;
        SwTableLines& rTableLines = pTable->GetTabLines();
        for (SwTableLine* pTableLine : rTableLines)
        {
            SwTableBoxes& rTableBoxes = pTableLine->GetTabBoxes();
            for (SwTableBox* pTableBox : rTableBoxes)
            {
                SwTableBoxFormat* pTableBoxFormat = pTableBox->GetFrameFormat();
                if (const SwTableBoxFormula* pBoxFormula = pTableBoxFormat->GetItemIfSet( RES_BOXATR_FORMULA, false ))
                {
                    const SwNode* pNd = pBoxFormula->GetNodeOfFormula();
                    if(!pNd || &pNd->GetNodes() != &pNd->GetDoc().GetNodes()) // is this ever valid or should we assert here?
                        continue;
                    rvFormulas.push_back(const_cast<SwTableBoxFormula*>(pBoxFormula));
                }
            }
        }
    }
}

void SwTable::Split(const UIName& sNewTableName, sal_uInt16 nSplitLine, SwHistory* pHistory)
{
    SwTableFormulaUpdate aHint(this);
    aHint.m_eFlags = TBL_SPLITTBL;
    aHint.m_aData.pNewTableNm = &sNewTableName;
    aHint.m_nSplitLine = nSplitLine;

    std::vector<SwTableBoxFormula*> vFormulas;
    GatherFormulas(vFormulas);
    for(auto pBoxFormula: vFormulas)
    {
        const SwNode* pNd = pBoxFormula->GetNodeOfFormula();
        const SwTableNode* pTableNd = pNd->FindTableNode();
        if(pTableNd == nullptr)
            continue;
        if(&pTableNd->GetTable() == this)
        {
            sal_uInt16 nLnPos = SwTableFormula::GetLnPosInTable(*this, pBoxFormula->GetTableBox());
            aHint.m_bBehindSplitLine = USHRT_MAX != nLnPos && aHint.m_nSplitLine <= nLnPos;
        }
        else
            aHint.m_bBehindSplitLine = false;
        pBoxFormula->ToSplitMergeBoxNmWithHistory(aHint, pHistory);
    }
}

void SwTable::Merge(const SwTable& rTable, SwHistory* pHistory)
{
    SwTableFormulaUpdate aHint(this);
    aHint.m_eFlags = TBL_MERGETBL;
    aHint.m_aData.pDelTable = &rTable;
    std::vector<SwTableBoxFormula*> vFormulas;
    GatherFormulas(vFormulas);
    for(auto pBoxFormula: vFormulas)
        pBoxFormula->ToSplitMergeBoxNmWithHistory(aHint, pHistory);
}

void SwTable::UpdateFields(TableFormulaUpdateFlags eFlags)
{
    SwDoc& rDoc = GetFrameFormat()->GetDoc();
    auto pFieldType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Table, OUString(), false);
    if(!pFieldType)
        return;
    std::vector<SwFormatField*> vFields;
    pFieldType->GatherFields(vFields);
    for(auto pFormatField : vFields)
    {
        SwTableField* pField = static_cast<SwTableField*>(pFormatField->GetField());
        // table where this field is located
        const SwTableNode* pTableNd;
        const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode();
        pTableNd = rTextNd.FindTableNode();
        if(pTableNd == nullptr || &pTableNd->GetTable() != this)
            continue;

        switch(eFlags)
        {
            case TBL_BOXNAME:
                // to the external representation
                pField->PtrToBoxNm(this);
                break;
            case TBL_RELBOXNAME:
                // to the relative representation
                pField->ToRelBoxNm(this);
                break;
            case TBL_BOXPTR:
                // to the internal representation
                // JP 17.06.96: internal representation on all formulas
                //              (reference to other table!!!)
                pField->BoxNmToPtr( &pTableNd->GetTable() );
                break;
            default:
                assert(false); // Only TBL_BOXNAME, TBL_RELBOXNAME and TBL_BOXPTR are supported
                break;
        }
    }

    // process all table box formulas
    SwTableLines& rTableLines = GetTabLines();
    for (SwTableLine* pTableLine : rTableLines)
    {
        SwTableBoxes& rTableBoxes = pTableLine->GetTabBoxes();
        for (SwTableBox* pTableBox : rTableBoxes)
        {
            SwTableBoxFormat* pTableBoxFormat = pTableBox->GetFrameFormat();
            if (const SwTableBoxFormula* pItem = pTableBoxFormat->GetItemIfSet( RES_BOXATR_FORMULA, false ))
            {
                // SwTableBoxFormula is non-shareable, so const_cast is somewhat OK
                auto & rBoxFormula = const_cast<SwTableBoxFormula&>(*pItem);
                if(eFlags == TBL_BOXPTR)
                    rBoxFormula.TryBoxNmToPtr();
                else if(eFlags == TBL_RELBOXNAME)
                    rBoxFormula.TryRelBoxNm();
                else
                    rBoxFormula.ChangeState();
            }
        }
    }
}

void SwTable::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTable"));
    (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p"this);
    (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("table-format"), "%p", GetFrameFormat());
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("lines"));
    for (const auto& pLine : m_aLines)
    {
        pLine->dumpAsXml(pWriter);
    }
    (void)xmlTextWriterEndElement(pWriter);
    (void)xmlTextWriterEndElement(pWriter);
}

// TODO Set HasTextChangesOnly=true, if needed based on the redlines in the cells.
// At tracked row deletion, return with the newest deletion of the row or
// at tracked row insertion, return with the oldest insertion in the row, which
// contain the change data of the row change.
// If the return value is SwRedlineTable::npos, there is no tracked row change.
SwRedlineTable::size_type SwTableLine::UpdateTextChangesOnly(
    SwRedlineTable::size_type& rRedlinePos, bool bUpdateProperty ) const
{
    SwRedlineTable::size_type nRet = SwRedlineTable::npos;
    const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc().getIDocumentRedlineAccess().GetRedlineTable();

    // check table row property "HasTextChangesOnly", if it's defined and its
    // value is false, and all text content is in delete redlines, the row is deleted
    const SvxPrintItem *pHasTextChangesOnlyProp =
            GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
    if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
    {
        const SwTableBoxes & rBoxes = GetTabBoxes();
        size_t nBoxes = rBoxes.size();
        bool bInsertion = false;
        bool bPlainTextInLine = false;
        SwRedlineTable::size_type nOldestRedline = SwRedlineTable::npos;
        SwRedlineTable::size_type nNewestRedline = SwRedlineTable::npos;
        for (size_t nBoxIndex = 0; nBoxIndex < nBoxes && rRedlinePos < aRedlineTable.size(); ++nBoxIndex)
        {
            auto pBox = rBoxes[nBoxIndex];
            if ( pBox->IsEmpty( /*bWithRemainingNestedTable =*/ false ) )
            {
               // no text content, check the next cells
               continue;
            }

            bool bHasRedlineInBox = false;
            SwPosition aCellStart( *pBox->GetSttNd(), SwNodeOffset(0) );
            SwPosition aCellEnd( *pBox->GetSttNd()->EndOfSectionNode(), SwNodeOffset(-1) );
            SwNodeIndex pEndNodeIndex(aCellEnd.GetNode());
            SwRangeRedline* pPreviousDeleteRedline = nullptr;
            for( ; rRedlinePos < aRedlineTable.size(); ++rRedlinePos )
            {
                const SwRangeRedline* pRedline = aRedlineTable[ rRedlinePos ];

                if ( pRedline->Start()->GetNodeIndex() > pEndNodeIndex.GetIndex() )
                {
                    // no more redlines in the actual cell,
                    // check the next ones
                    break;
                }

                // redline in the cell
                if ( aCellStart <= *pRedline->Start() )
                {
                    if ( !bHasRedlineInBox )
                    {
                        bHasRedlineInBox = true;
                        // plain text before the first redline in the text
                        if ( pRedline->Start()->GetContentIndex() > 0 )
                            bPlainTextInLine = true;
                    }

                    RedlineType nType = pRedline->GetType();

                    // first insert redline
                    if ( !bInsertion )
                    {
                        if ( RedlineType::Insert == nType )
                        {
                            bInsertion = true;
                        }
                        else
                        {
                            // plain text between the delete redlines
--> --------------------

--> maximum size reached

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

Messung V0.5
C=93 H=82 G=87

¤ Dauer der Verarbeitung: 0.35 Sekunden  (vorverarbeitet)  ¤

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