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

#include <memory>
#include <com/sun/star/text/HoriOrientation.hpp>
#include <officecfg/Office/Writer.hxx>
#include <osl/diagnose.h>
#include <svl/numformat.hxx>
#include <hintids.hxx>

#include <editeng/lrspitem.hxx>
#include <editeng/boxitem.hxx>
#include <tools/fract.hxx>
#include <fmtfsize.hxx>
#include <fmtornt.hxx>
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentChartDataProviderAccess.hxx>
#include <DocumentContentOperationsManager.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <docsh.hxx>
#include <fesh.hxx>
#include <tabfrm.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <pam.hxx>
#include <swtable.hxx>
#include <tblsel.hxx>
#include <fldbas.hxx>
#include <rowfrm.hxx>
#include <ddefld.hxx>
#include <hints.hxx>
#include <UndoTable.hxx>
#include <cellatr.hxx>
#include <mvsave.hxx>
#include <swtblfmt.hxx>
#include <swddetbl.hxx>
#include <poolfmt.hxx>
#include <tblrwcl.hxx>
#include <unochart.hxx>
#include <o3tl/numeric.hxx>
#include <calbck.hxx>
#include <docary.hxx>

using namespace com::sun::star;

#define COLFUZZY 20
#define ROWFUZZY 10

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

namespace {

// In order to set the Frame Formats for the Boxes, it's enough to look
// up the current one in the array. If it's already there return the new one.
struct CpyTabFrame
{
    SwFrameFormat* pFrameFormat;
    SwTableBoxFormat *pNewFrameFormat;

    explicit CpyTabFrame(SwFrameFormat* pCurrentFrameFormat) : pNewFrameFormat( nullptr )
    {   pFrameFormat = pCurrentFrameFormat; }

    bool operator==( const CpyTabFrame& rCpyTabFrame ) const
        { return pFrameFormat == rCpyTabFrame.pFrameFormat; }
    bool operator<( const CpyTabFrame& rCpyTabFrame ) const
        { return pFrameFormat < rCpyTabFrame.pFrameFormat; }
};

struct CR_SetBoxWidth
{
    SwShareBoxFormats aShareFormats;
    SwTableNode* pTableNd;
    SwTwips nDiff, nSide, nMaxSize, nLowerDiff;
    TableChgMode nMode;
    bool bBigger, bLeft;

    CR_SetBoxWidth( TableChgWidthHeightType eType, SwTwips nDif, SwTwips nSid,
                    SwTwips nMax, SwTableNode* pTNd )
        : pTableNd( pTNd ),
        nDiff( nDif ), nSide( nSid ), nMaxSize( nMax ), nLowerDiff( 0 )
    {
        bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) ||
                TableChgWidthHeightType::CellLeft == extractPosition( eType );
        bBigger = bool(eType & TableChgWidthHeightType::BiggerMode );
        nMode = pTableNd->GetTable().GetTableChgMode();
    }
    CR_SetBoxWidth( const CR_SetBoxWidth& rCpy )
        : pTableNd( rCpy.pTableNd ),
        nDiff( rCpy.nDiff ), nSide( rCpy.nSide ),
        nMaxSize( rCpy.nMaxSize ), nLowerDiff( 0 ),
        nMode( rCpy.nMode ),
        bBigger( rCpy.bBigger ), bLeft( rCpy.bLeft )
    {
    }

    void LoopClear()
    {
        nLowerDiff = 0;
    }
};

}

static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
                         SwTwips nDist, bool bCheck );
static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
                                SwTwips nDist, bool bCheck );

typedef bool (*FN_lcl_SetBoxWidth)(SwTableLine*, CR_SetBoxWidth&, SwTwips, bool );

namespace {

struct CR_SetLineHeight
{
    SwTableNode* pTableNd;
    SwTwips nMaxSpace, nMaxHeight;
    TableChgMode nMode;
    bool bBigger;

    CR_SetLineHeight( TableChgWidthHeightType eType, SwTableNode* pTNd )
        : pTableNd( pTNd ),
        nMaxSpace( 0 ), nMaxHeight( 0 )
    {
        bBigger = bool(eType & TableChgWidthHeightType::BiggerMode );
        nMode = pTableNd->GetTable().GetTableChgMode();
    }
    CR_SetLineHeight( const CR_SetLineHeight& rCpy )
        : pTableNd( rCpy.pTableNd ),
        nMaxSpace( rCpy.nMaxSpace ), nMaxHeight( rCpy.nMaxHeight ),
        nMode( rCpy.nMode ),
        bBigger( rCpy.bBigger )
    {}
};

}

static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
                         SwTwips nDist, bool bCheck );
static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
                                SwTwips nDist, bool bCheck );

typedef bool (*FN_lcl_SetLineHeight)(SwTableLine*, CR_SetLineHeight&, SwTwips, bool );

typedef o3tl::sorted_vector<CpyTabFrame> CpyTabFrames;

namespace {

struct CpyPara
{
    std::shared_ptr< std::vector< std::vector< sal_uLong > > > pWidths;
    SwDoc& rDoc;
    SwTableNode* pTableNd;
    CpyTabFrames& rTabFrameArr;
    SwTableLine* pInsLine;
    SwTableBox* pInsBox;
    sal_uLong nOldSize, nNewSize;           // in order to correct the size attributes
    sal_uLong nMinLeft, nMaxRight;
    sal_uInt16 nCpyCnt, nInsPos;
    sal_uInt16 nLnIdx, nBoxIdx;
    sal_uInt8 nDelBorderFlag;
    bool bCpyContent;

    CpyPara( SwTableNode* pNd, sal_uInt16 nCopies, CpyTabFrames& rFrameArr )
        : rDoc( pNd->GetDoc() ), pTableNd( pNd ), rTabFrameArr(rFrameArr),
        pInsLine(nullptr), pInsBox(nullptr), nOldSize(0), nNewSize(0),
        nMinLeft(ULONG_MAX), nMaxRight(0),
        nCpyCnt(nCopies), nInsPos(0),
        nLnIdx(0), nBoxIdx(0),
        nDelBorderFlag(0), bCpyContent( true )
        {}
    CpyPara( const CpyPara& rPara, SwTableLine* pLine )
        : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd),
        rTabFrameArr(rPara.rTabFrameArr), pInsLine(pLine), pInsBox(rPara.pInsBox),
        nOldSize(0), nNewSize(rPara.nNewSize), nMinLeft( rPara.nMinLeft ),
        nMaxRight( rPara.nMaxRight ), nCpyCnt(rPara.nCpyCnt), nInsPos(0),
        nLnIdx( rPara.nLnIdx), nBoxIdx( rPara.nBoxIdx ),
        nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent )
        {}
    CpyPara( const CpyPara& rPara, SwTableBox* pBox )
        : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd),
        rTabFrameArr(rPara.rTabFrameArr), pInsLine(rPara.pInsLine), pInsBox(pBox),
        nOldSize(rPara.nOldSize), nNewSize(rPara.nNewSize),
        nMinLeft( rPara.nMinLeft ), nMaxRight( rPara.nMaxRight ),
        nCpyCnt(rPara.nCpyCnt), nInsPos(0), nLnIdx(rPara.nLnIdx), nBoxIdx(rPara.nBoxIdx),
        nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent )
        {}
};

}

static SwTableLine* lcl_CopyRow(FndLine_ & rFndLine, CpyPara *const pCpyPara);

static void lcl_CopyCol( FndBox_ & rFndBox, CpyPara *const pCpyPara)
{
    // Look up the Frame Format in the Frame Format Array
    SwTableBox* pBox = rFndBox.GetBox();
    CpyTabFrame aFindFrame(pBox->GetFrameFormat());

    if( pCpyPara->nCpyCnt )
    {
        sal_uInt16 nFndPos;
        CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame );
        nFndPos = itFind - pCpyPara->rTabFrameArr.begin();
        if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) )
        {
            // For nested copying, also save the new Format as an old one.
            SwTableBoxFormat* pNewFormat = pBox->ClaimFrameFormat();

            // Find the selected Boxes in the Line:
            FndLine_ const* pCmpLine = nullptr;
            SwFormatFrameSize aFrameSz( pNewFormat->GetFrameSize() );

            bool bDiffCount = false;
            if( !pBox->GetTabLines().empty() )
            {
                pCmpLine = rFndBox.GetLines().front().get();
                if ( pCmpLine->GetBoxes().size() != pCmpLine->GetLine()->GetTabBoxes().size() )
                    bDiffCount = true;
            }

            if( bDiffCount )
            {
                // The first Line should be enough
                FndBoxes_t const& rFndBoxes = pCmpLine->GetBoxes();
                tools::Long nSz = 0;
                for( auto n = rFndBoxes.size(); n; )
                {
                    nSz += rFndBoxes[--n]->GetBox()->
                            GetFrameFormat()->GetFrameSize().GetWidth();
                }
                aFrameSz.SetWidth( aFrameSz.GetWidth() -
                                            nSz / ( pCpyPara->nCpyCnt + 1 ) );
                pNewFormat->SetFormatAttr( aFrameSz );
                aFrameSz.SetWidth( nSz / ( pCpyPara->nCpyCnt + 1 ) );

                // Create a new Format for the new Box, specifying its size.
                aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pNewFormat->GetDoc().
                                            MakeTableLineFormat());
                *aFindFrame.pNewFrameFormat = *pNewFormat;
                aFindFrame.pNewFrameFormat->SetFormatAttr( aFrameSz );
            }
            else
            {
                aFrameSz.SetWidth( aFrameSz.GetWidth() / ( pCpyPara->nCpyCnt + 1 ) );
                pNewFormat->SetFormatAttr( aFrameSz );

                aFindFrame.pNewFrameFormat = pNewFormat;
                pCpyPara->rTabFrameArr.insert( aFindFrame );
                aFindFrame.pFrameFormat = pNewFormat;
                pCpyPara->rTabFrameArr.insert( aFindFrame );
            }
        }
        else
        {
            aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ];
            pBox->ChgFrameFormat( aFindFrame.pNewFrameFormat );
        }
    }
    else
    {
        CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame );
        if( pCpyPara->nDelBorderFlag &&
            itFind != pCpyPara->rTabFrameArr.end() )
            aFindFrame = *itFind;
        else
            aFindFrame.pNewFrameFormat = pBox->GetFrameFormat();
    }

    if (!rFndBox.GetLines().empty())
    {
        pBox = new SwTableBox( aFindFrame.pNewFrameFormat,
                    rFndBox.GetLines().size(), pCpyPara->pInsLine );
        pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox );
        CpyPara aPara( *pCpyPara, pBox );
        aPara.nDelBorderFlag &= 7;

        for (auto const& pFndLine : rFndBox.GetLines())
        {
            lcl_CopyRow(*pFndLine, &aPara);
        }
    }
    else
    {
        ::InsTableBox( pCpyPara->rDoc, pCpyPara->pTableNd, pCpyPara->pInsLine,
                    aFindFrame.pNewFrameFormat, pBox, pCpyPara->nInsPos++ );

        const FndBoxes_t& rFndBxs = rFndBox.GetUpper()->GetBoxes();
        if( 8 > pCpyPara->nDelBorderFlag
                ? pCpyPara->nDelBorderFlag != 0
                : &rFndBox == rFndBxs[rFndBxs.size() - 1].get())
        {
            const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
            if( 8 > pCpyPara->nDelBorderFlag
                    ? rBoxItem.GetTop()
                    : rBoxItem.GetRight() )
            {
                aFindFrame.pFrameFormat = pBox->GetFrameFormat();

                SvxBoxItem aNew( rBoxItem );
                if( 8 > pCpyPara->nDelBorderFlag )
                    aNew.SetLine( nullptr, SvxBoxItemLine::TOP );
                else
                    aNew.SetLine( nullptr, SvxBoxItemLine::RIGHT );

                if( 1 == pCpyPara->nDelBorderFlag ||
                    8 == pCpyPara->nDelBorderFlag )
                {
                    // For all Boxes that delete TopBorderLine, we copy after that
                    pBox = pCpyPara->pInsLine->GetTabBoxes()[
                                            pCpyPara->nInsPos - 1 ];
                }

                aFindFrame.pNewFrameFormat = pBox->GetFrameFormat();

                // Else we copy before that and the first Line keeps the TopLine
                // and we remove it at the original
                pBox->ClaimFrameFormat()->SetFormatAttr( aNew );

                if( !pCpyPara->nCpyCnt )
                    pCpyPara->rTabFrameArr.insert( aFindFrame );
            }
        }
    }
}

static SwTableLine* lcl_CopyRow(FndLine_& rFndLine, CpyPara *const pCpyPara)
{
    SwTableLine* pNewLine = new SwTableLine(
                        rFndLine.GetLine()->GetFrameFormat(),
                        rFndLine.GetBoxes().size(), pCpyPara->pInsBox );
    if( pCpyPara->pInsBox )
    {
        SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines();
        rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
    }
    else
    {
        SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines();
        rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
    }

    CpyPara aPara( *pCpyPara, pNewLine );
    for (auto const& it : rFndLine.GetBoxes())
    {
        lcl_CopyCol(*it, &aPara);
    }

    pCpyPara->nDelBorderFlag &= 0xf8;

    return pNewLine;
}

static void lcl_InsCol( FndLine_* pFndLn, CpyPara& rCpyPara, sal_uInt16 nCpyCnt,
                bool bBehind )
{
    // Bug 29124: Not only copy in the BaseLines. If possible, we go down as far as possible
    FndBox_* pFBox;
    if( 1 == pFndLn->GetBoxes().size() &&
        !( pFBox = pFndLn->GetBoxes()[0].get() )->GetBox()->GetSttNd() )
    {
        // A Box with multiple Lines, so insert into these Lines
        for (auto &rpLine : pFBox->GetLines())
        {
            lcl_InsCol( rpLine.get(), rCpyPara, nCpyCnt, bBehind );
        }
    }
    else
    {
        rCpyPara.pInsLine = pFndLn->GetLine();
        SwTableBox* pBox = pFndLn->GetBoxes()[ bBehind ?
                    pFndLn->GetBoxes().size()-1 : 0 ]->GetBox();
        rCpyPara.nInsPos = pFndLn->GetLine()->GetBoxPos( pBox );
        if( bBehind )
            ++rCpyPara.nInsPos;

        for( sal_uInt16 n = 0; n < nCpyCnt; ++n )
        {
            if( n + 1 == nCpyCnt && bBehind )
                rCpyPara.nDelBorderFlag = 9;
            else
                rCpyPara.nDelBorderFlag = 8;
            for (auto const& it : pFndLn->GetBoxes())
            {
                lcl_CopyCol(*it, &rCpyPara);
            }
        }
    }
}

static SwRowFrame* GetRowFrame( SwTableLine& rLine )
{
    SwIterator<SwRowFrame,SwFormat> aIter( *rLine.GetFrameFormat() );
    for( SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
        if( pFrame->GetTabLine() == &rLine )
            return pFrame;
    return nullptr;
}

bool SwTable::InsertCol( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
    bool bBehind, bool bInsertDummy )
{
    OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box List" );
    SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
    if( !pTableNd )
        return false;

    bool bRes = true;
    if( IsNewModel() )
        bRes = NewInsertCol( rDoc, rBoxes, nCnt, bBehind, bInsertDummy );
    else
    {
        // Find all Boxes/Lines
        FndBox_ aFndBox( nullptr, nullptr );
        {
            FndPara aPara( rBoxes, &aFndBox );
            ForEach_FndLineCopyCol( GetTabLines(), &aPara );
        }
        if( aFndBox.GetLines().empty() )
            return false;

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

        // Find Lines for the layout update
        aFndBox.SetTableLines( *this );
        aFndBox.DelFrames( *this );

        // TL_CHART2: nothing to be done since chart2 currently does not want to
        // get notified about new rows/cols.

        CpyTabFrames aTabFrameArr;
        CpyPara aCpyPara( pTableNd, nCnt, aTabFrameArr );

        for (auto & rpLine : aFndBox.GetLines())
        {
            lcl_InsCol( rpLine.get(), aCpyPara, nCnt, bBehind );
        }

        // clean up this Line's structure once again, generally all of them
        GCLines();

        // Update Layout
        aFndBox.MakeFrames( *this );

#if defined DBG_UTIL
        CheckBoxWidth(GetTabLines(), *GetFrameFormat());;
        CheckTableLayout(GetTabLines());
#endif

        bRes = true;
    }

    SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
    if (pPCD && nCnt)
        pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind );
    rDoc.UpdateCharts( GetFrameFormat()->GetName() );

    if (SwFEShell* pFEShell = rDoc.GetDocShell()->GetFEShell())
    {
        if (officecfg::Office::Writer::Table::Change::ApplyTableAutoFormat::get())
        {
            pFEShell->UpdateTableStyleFormatting();
        }
    }

    return bRes;
}

bool SwTable::InsertRow_( SwDoc& rDoc, const SwSelBoxes& rBoxes,
                        sal_uInt16 nCnt, bool bBehind, bool bInsertDummy )
{
    OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box List" );
    SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
    if( !pTableNd )
        return false;

     // Find all Boxes/Lines
    FndBox_ aFndBox( nullptr, nullptr );
    {
        FndPara aPara( rBoxes, &aFndBox );
        ForEach_FndLineCopyCol( GetTabLines(), &aPara );
    }
    if( aFndBox.GetLines().empty() )
        return false;

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

    FndBox_* pFndBox = &aFndBox;
    {
        FndLine_* pFndLine;
        while( 1 == pFndBox->GetLines().size() )
        {
            pFndLine = pFndBox->GetLines()[0].get();
            if( 1 != pFndLine->GetBoxes().size() )
                break;
            // Don't go down too far! One Line with Box needs to remain!
            FndBox_ *const pTmpBox = pFndLine->GetBoxes().front().get();
            if( !pTmpBox->GetLines().empty() )
                pFndBox = pTmpBox;
            else
                break;
        }
    }

    // Find Lines for the layout update
    const bool bLayout = !IsNewModel() &&
        nullptr != SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First();

    if ( bLayout )
    {
        aFndBox.SetTableLines( *this );
        if( pFndBox != &aFndBox )
            aFndBox.DelFrames( *this );
        // TL_CHART2: nothing to be done since chart2 currently does not want to
        // get notified about new rows/cols.
    }

    CpyTabFrames aTabFrameArr;
    CpyPara aCpyPara( pTableNd, 0, aTabFrameArr );

    SwTableLine* pLine = pFndBox->GetLines()[ bBehind ?
                    pFndBox->GetLines().size()-1 : 0 ]->GetLine();
    if( &aFndBox == pFndBox )
        aCpyPara.nInsPos = GetTabLines().GetPos( pLine );
    else
    {
        aCpyPara.pInsBox = pFndBox->GetBox();
        aCpyPara.nInsPos = pFndBox->GetBox()->GetTabLines().GetPos( pLine );
    }

    if( bBehind )
    {
        ++aCpyPara.nInsPos;
        aCpyPara.nDelBorderFlag = 1;
    }
    else
        aCpyPara.nDelBorderFlag = 2;

    for( sal_uInt16 nCpyCnt = 0; nCpyCnt < nCnt; ++nCpyCnt )
    {
        if( bBehind )
            aCpyPara.nDelBorderFlag = 1;
        for (auto & rpFndLine : pFndBox->GetLines())
        {
            SwTableLine* pNewTableLine = lcl_CopyRow( *rpFndLine, &aCpyPara );

            // tracked insertion of empty table line
            if ( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
            {
                SvxPrintItem aSetTracking(RES_PRINT, false);
                SwPosition aPos(*pNewTableLine->GetTabBoxes()[0]->GetSttNd());
                SwCursor aCursor( aPos, nullptr );
                if ( bInsertDummy )
                {
                    SwPaM aPaM(*pNewTableLine->GetTabBoxes()[0]->GetSttNd(), SwNodeOffset(1));
                    rDoc.getIDocumentContentOperations().InsertString( aPaM,
                        OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
                }
                rDoc.SetRowNotTracked( aCursor, aSetTracking, /*bAll=*/false, /*bIns=*/true );
            }
        }
    }

    // clean up this Line's structure once again, generally all of them
    if( !rDoc.IsInReading() )
        GCLines();

    // Update Layout
    if ( bLayout )
    {
        if( pFndBox != &aFndBox )
            aFndBox.MakeFrames( *this );
        else
            aFndBox.MakeNewFrames( *this, nCnt, bBehind );
    }

#if defined DBG_UTIL
    CheckBoxWidth(GetTabLines(), *GetFrameFormat());;
    CheckTableLayout(GetTabLines());
#endif

    SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
    if (pPCD && nCnt)
        pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind );
    rDoc.UpdateCharts( GetFrameFormat()->GetName() );

    if (SwFEShell* pFEShell = rDoc.GetDocShell()->GetFEShell())
    {
        if (officecfg::Office::Writer::Table::Change::ApplyTableAutoFormat::get())
        {
            pFEShell->UpdateTableStyleFormatting(pTableNd);
        }
    }

    return true;
}

static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset,
                            bool bFirst, SwShareBoxFormats& rShareFormats );

static void lcl_LastBoxSetWidthLine( SwTableLines &rLines, const tools::Long nOffset,
                                bool bFirst, SwShareBoxFormats& rShareFormats )
{
    for ( auto pLine : rLines )
        ::lcl_LastBoxSetWidth( pLine->GetTabBoxes(), nOffset, bFirst, rShareFormats );
}

static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset,
                            bool bFirst, SwShareBoxFormats& rShareFormats )
{
    SwTableBox& rBox = *(bFirst ? rBoxes.front() : rBoxes.back());
    if( !rBox.GetSttNd() )
        ::lcl_LastBoxSetWidthLine( rBox.GetTabLines(), nOffset,
                                    bFirst, rShareFormats );

    // Adapt the Box
    const SwFrameFormat *pBoxFormat = rBox.GetFrameFormat();
    SwFormatFrameSize aNew( pBoxFormat->GetFrameSize() );
    aNew.SetWidth( aNew.GetWidth() + nOffset );
    SwFrameFormat *pFormat = rShareFormats.GetFormat( *pBoxFormat, aNew );
    if( pFormat )
        rBox.ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFormat) );
    else
    {
        pFormat = rBox.ClaimFrameFormat();

        pFormat->LockModify();
        pFormat->SetFormatAttr( aNew );
        pFormat->UnlockModify();

        rShareFormats.AddFormat( *pBoxFormat, *pFormat );
    }
}

void DeleteBox_( SwTable& rTable, SwTableBox* pBox, SwUndo* pUndo,
                bool bCalcNewSize, const bool bCorrBorder,
                SwShareBoxFormats* pShareFormats )
{
    do {
        SwTwips nBoxSz = bCalcNewSize ?
                pBox->GetFrameFormat()->GetFrameSize().GetWidth() : 0;
        SwTableLine* pLine = pBox->GetUpper();
        SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
        sal_uInt16 nDelPos = pLine->GetBoxPos( pBox );
        SwTableBox* pUpperBox = pBox->GetUpper()->GetUpper();

        // Special treatment for the border:
        if( bCorrBorder && 1 < rTableBoxes.size() )
        {
            const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();

            if( rBoxItem.GetLeft() || rBoxItem.GetRight() )
            {
                bool bChgd = false;

                // JP 02.04.97: 1st part for Bug 36271
                // First the left/right edges
                if( nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) )
                {
                    SwTableBox* pNxtBox = rTableBoxes[ nDelPos + 1 ];
                    const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox();

                    SwTableBox* pPrvBox = nDelPos ? rTableBoxes[ nDelPos - 1 ] : nullptr;

                    if( pNxtBox->GetSttNd() && !rNxtBoxItem.GetLeft() &&
                        ( !pPrvBox || !pPrvBox->GetFrameFormat()->GetBox().GetRight()) )
                    {
                        SvxBoxItem aTmp( rNxtBoxItem );
                        aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft()
                                                         : rBoxItem.GetRight(),
                                                            SvxBoxItemLine::LEFT );
                        if( pShareFormats )
                            pShareFormats->SetAttr( *pNxtBox, aTmp );
                        else
                            pNxtBox->ClaimFrameFormat()->SetFormatAttr( aTmp );
                        bChgd = true;
                    }
                }
                if( !bChgd && nDelPos )
                {
                    SwTableBox* pPrvBox = rTableBoxes[ nDelPos - 1 ];
                    const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox();

                    SwTableBox* pNxtBox = nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size())
                                            ? rTableBoxes[ nDelPos + 1 ] : nullptr;

                    if( pPrvBox->GetSttNd() && !rPrvBoxItem.GetRight() &&
                        ( !pNxtBox || !pNxtBox->GetFrameFormat()->GetBox().GetLeft()) )
                    {
                        SvxBoxItem aTmp( rPrvBoxItem );
                        aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft()
                                                         : rBoxItem.GetRight(),
                                                            SvxBoxItemLine::RIGHT );
                        if( pShareFormats )
                            pShareFormats->SetAttr( *pPrvBox, aTmp );
                        else
                            pPrvBox->ClaimFrameFormat()->SetFormatAttr( aTmp );
                    }
                }
            }
        }

        // Delete the Box first, then the Nodes!
        SwStartNode* pSttNd = const_cast<SwStartNode*>(pBox->GetSttNd());
        if( pShareFormats )
            pShareFormats->RemoveFormat( *rTableBoxes[ nDelPos ]->GetFrameFormat() );

        // Before deleting the 'Table Box' from memory - delete any redlines attached to it
        rTable.GetFrameFormat()->GetDoc().getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableCellRedline( rTable.GetFrameFormat()->GetDoc(), *(rTableBoxes[nDelPos]), true, RedlineType::Any );
        delete rTableBoxes[nDelPos];
        rTableBoxes.erase( rTableBoxes.begin() + nDelPos );

        if( pSttNd )
        {
            // Has the UndoObject been prepared to save the Section?
            if( pUndo && pUndo->IsDelBox() )
                static_cast<SwUndoTableNdsChg*>(pUndo)->SaveSection( pSttNd );
            else
                pSttNd->GetDoc().getIDocumentContentOperations().DeleteSection( pSttNd );
        }

        // Also delete the Line?
        if( !rTableBoxes.empty() )
        {
            // Then adapt the Frame-SSize
            bool bLastBox = nDelPos == rTableBoxes.size();
            if( bLastBox )
                --nDelPos;
            pBox = rTableBoxes[nDelPos];
            if( bCalcNewSize )
            {
                SwFormatFrameSize aNew( pBox->GetFrameFormat()->GetFrameSize() );
                aNew.SetWidth( aNew.GetWidth() + nBoxSz );
                if( pShareFormats )
                    pShareFormats->SetSize( *pBox, aNew );
                else
                    pBox->ClaimFrameFormat()->SetFormatAttr( aNew );

                if( !pBox->GetSttNd() )
                {
                    // We need to this recursively in all Lines in all Cells!
                    SwShareBoxFormats aShareFormats;
                    ::lcl_LastBoxSetWidthLine( pBox->GetTabLines(), nBoxSz,
                                                !bLastBox,
                                                pShareFormats ? *pShareFormats
                                                           : aShareFormats );
                }
            }
            break;      // Stop deleting
        }
        // Delete the Line from the Table/Box
        if( !pUpperBox )
        {
            // Also delete the Line from the Table
            nDelPos = rTable.GetTabLines().GetPos( pLine );
            if( pShareFormats )
                pShareFormats->RemoveFormat( *rTable.GetTabLines()[ nDelPos ]->GetFrameFormat() );

            SwTableLine* pTabLineToDelete = rTable.GetTabLines()[ nDelPos ];
            // Before deleting the 'Table Line' from memory - delete any redlines attached to it
            rTable.GetFrameFormat()->GetDoc().getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any );
            delete pTabLineToDelete;
            rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nDelPos );
            break;      // we cannot delete more
        }

        // finally also delete the Line
        pBox = pUpperBox;
        nDelPos = pBox->GetTabLines().GetPos( pLine );
        if( pShareFormats )
            pShareFormats->RemoveFormat( *pBox->GetTabLines()[ nDelPos ]->GetFrameFormat() );

        SwTableLine* pTabLineToDelete = pBox->GetTabLines()[ nDelPos ];
        // Before deleting the 'Table Line' from memory - delete any redlines attached to it
        rTable.GetFrameFormat()->GetDoc().getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any );
        delete pTabLineToDelete;
        pBox->GetTabLines().erase( pBox->GetTabLines().begin() + nDelPos );
    } while( pBox->GetTabLines().empty() );
}

static SwTableBox*
lcl_FndNxtPrvDelBox( const SwTableLines& rTableLns,
                                SwTwips nBoxStt, SwTwips nBoxWidth,
                                sal_uInt16 nLinePos, bool bNxt,
                                SwSelBoxes* pAllDelBoxes, size_t *const pCurPos)
{
    SwTableBox* pFndBox = nullptr;
    do {
        if( bNxt )
            ++nLinePos;
        else
            --nLinePos;
        SwTableLine* pLine = rTableLns[ nLinePos ];
        SwTwips nFndBoxWidth = 0;
        SwTwips nFndWidth = nBoxStt + nBoxWidth;

        pFndBox = pLine->GetTabBoxes()[ 0 ];
        for( auto pBox : pLine->GetTabBoxes() )
        {
            if ( nFndWidth <= 0 )
            {
                break;
            }
            pFndBox = pBox;
            nFndBoxWidth = pFndBox->GetFrameFormat()->GetFrameSize().GetWidth();
            nFndWidth -= nFndBoxWidth;
        }

        // Find the first ContentBox
        while( !pFndBox->GetSttNd() )
        {
            const SwTableLines& rLowLns = pFndBox->GetTabLines();
            if( bNxt )
                pFndBox = rLowLns.front()->GetTabBoxes().front();
            else
                pFndBox = rLowLns.back()->GetTabBoxes().front();
        }

        if( std::abs( nFndWidth ) > COLFUZZY ||
            std::abs( nBoxWidth - nFndBoxWidth ) > COLFUZZY )
            pFndBox = nullptr;
        else if( pAllDelBoxes )
        {
            // If the predecessor will also be deleted, there's nothing to do
            SwSelBoxes::const_iterator aFndIt = pAllDelBoxes->find( pFndBox);
            if( aFndIt == pAllDelBoxes->end() )
                break;
            size_t const nFndPos = aFndIt - pAllDelBoxes->begin() ;

            // else, we keep on searching.
            // We do not need to recheck the Box, however
            pFndBox = nullptr;
            if( nFndPos <= *pCurPos )
                --*pCurPos;
            pAllDelBoxes->erase( pAllDelBoxes->begin() + nFndPos );
        }
    } while( bNxt ? ( nLinePos + 1 < o3tl::narrowing<sal_uInt16>(rTableLns.size()) ) : nLinePos != 0 );
    return pFndBox;
}

static void
lcl_SaveUpperLowerBorder( SwTable& rTable, const SwTableBox& rBox,
                                SwShareBoxFormats& rShareFormats,
                                SwSelBoxes* pAllDelBoxes = nullptr,
                                size_t *const pCurPos = nullptr )
{
//JP 16.04.97:  2. part for Bug 36271
    const SwTableLine* pLine = rBox.GetUpper();
    const SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
    const SwTableBox* pUpperBox = &rBox;
    sal_uInt16 nDelPos = pLine->GetBoxPos( pUpperBox );
    pUpperBox = rBox.GetUpper()->GetUpper();
    const SvxBoxItem& rBoxItem = rBox.GetFrameFormat()->GetBox();

    // then the top/bottom edges
    if( !rBoxItem.GetTop() && !rBoxItem.GetBottom() )
        return;

    bool bChgd = false;
    const SwTableLines* pTableLns;
    if( pUpperBox )
        pTableLns = &pUpperBox->GetTabLines();
    else
        pTableLns = &rTable.GetTabLines();

    sal_uInt16 nLnPos = pTableLns->GetPos( pLine );

    // Calculate the attribute position of the top-be-deleted Box and then
    // search in the top/bottom Line of the respective counterparts.
    SwTwips nBoxStt = 0;
    for( sal_uInt16 n = 0; n < nDelPos; ++n )
        nBoxStt += rTableBoxes[ n ]->GetFrameFormat()->GetFrameSize().GetWidth();
    SwTwips nBoxWidth = rBox.GetFrameFormat()->GetFrameSize().GetWidth();

    SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
    if( nLnPos )        // Predecessor?
        pPrvBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth,
                            nLnPos, false, pAllDelBoxes, pCurPos );

    if( nLnPos + 1 < o3tl::narrowing<sal_uInt16>(pTableLns->size()) )     // Successor?
        pNxtBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth,
                            nLnPos, true, pAllDelBoxes, pCurPos );

    if( pNxtBox && pNxtBox->GetSttNd() )
    {
        const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox();
        if( !rNxtBoxItem.GetTop() && ( !pPrvBox ||
            !pPrvBox->GetFrameFormat()->GetBox().GetBottom()) )
        {
            SvxBoxItem aTmp( rNxtBoxItem );
            aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop()
                                            : rBoxItem.GetBottom(),
                                            SvxBoxItemLine::TOP );
            rShareFormats.SetAttr( *pNxtBox, aTmp );
            bChgd = true;
        }
    }
    if( !(!bChgd && pPrvBox && pPrvBox->GetSttNd()) )
        return;

    const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox();
    if( !rPrvBoxItem.GetTop() && ( !pNxtBox ||
        !pNxtBox->GetFrameFormat()->GetBox().GetTop()) )
    {
        SvxBoxItem aTmp( rPrvBoxItem );
        aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop()
                                        : rBoxItem.GetBottom(),
                                        SvxBoxItemLine::BOTTOM );
        rShareFormats.SetAttr( *pPrvBox, aTmp );
    }

}

bool SwTable::DeleteSel(
    SwDoc&     rDoc
    ,
    const SwSelBoxes& rBoxes,
    const SwSelBoxes* pMerged, SwUndo* pUndo,
    const bool bDelMakeFrames, const bool bCorrBorder )
{
    SwTableNode* pTableNd = nullptr;
    if( !rBoxes.empty() )
    {
        pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
        if( !pTableNd )
            return false;
    }

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

    // Find Lines for the Layout update
    FndBox_ aFndBox( nullptr, nullptr );
    if ( bDelMakeFrames )
    {
        if( pMerged && !pMerged->empty() )
            aFndBox.SetTableLines( *pMerged, *this );
        else if( !rBoxes.empty() )
            aFndBox.SetTableLines( rBoxes, *this );
        aFndBox.DelFrames( *this );
    }

    SwShareBoxFormats aShareFormats;

    // First switch the Border, then delete
    if( bCorrBorder )
    {
        SwSelBoxes aBoxes( rBoxes );
        for (size_t n = 0; n < aBoxes.size(); ++n)
        {
            ::lcl_SaveUpperLowerBorder( *this, *rBoxes[ n ], aShareFormats,
                                        &aBoxes, &n );
        }
    }

    PrepareDelBoxes( rBoxes );

    SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
    // Delete boxes from last to first
    for (size_t n = 0; n < rBoxes.size(); ++n)
    {
        size_t const nIdx = rBoxes.size() - 1 - n;

        // First adapt the data-sequence for chart if necessary
        // (needed to move the implementation cursor properly to its new
        // position which can't be done properly if the cell is already gone)
        if (pPCD && pTableNd)
            pPCD->DeleteBox( &pTableNd->GetTable(), *rBoxes[nIdx] );

        // ... then delete the boxes
        DeleteBox_( *this, rBoxes[nIdx], pUndo, true, bCorrBorder, &aShareFormats );
    }

    // then clean up the structure of all Lines
    GCLines();

    if( bDelMakeFrames && aFndBox.AreLinesToRestore( *this ) )
        aFndBox.MakeFrames( *this );

    // TL_CHART2: now inform chart that sth has changed
    rDoc.UpdateCharts( GetFrameFormat()->GetName() );

#if defined DBG_UTIL
    CheckTableLayout(GetTabLines());
#endif
    CHECK_TABLE( *this );

    return true;
}

bool SwTable::OldSplitRow( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
                        bool bSameHeight )
{
    OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" );
    SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
    if( !pTableNd )
        return false;

    // TL_CHART2: splitting/merging of a number of cells or rows will usually make
    // the table too complex to be handled with chart.
    // Thus we tell the charts to use their own data provider and forget about this table
    rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );

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

    // If the rows should get the same (min) height, we first have
    // to store the old row heights before deleting the frames
    std::unique_ptr<tools::Long[]> pRowHeights;
    if ( bSameHeight )
    {
        pRowHeights.reset(new tools::Long[ rBoxes.size() ]);
        for (size_t n = 0; n < rBoxes.size(); ++n)
        {
            SwTableBox* pSelBox = rBoxes[n];
            const SwRowFrame* pRow = GetRowFrame( *pSelBox->GetUpper() );
            assert(pRow && "Where is the SwTableLine's Frame?");
            SwRectFnSet aRectFnSet(*pRow);
            pRowHeights[ n ] = aRectFnSet.GetHeight(pRow->getFrameArea());
        }
    }

    // Find Lines for the Layout update
    FndBox_ aFndBox( nullptr, nullptr );
    aFndBox.SetTableLines( rBoxes, *this );
    aFndBox.DelFrames( *this );

    for (size_t n = 0; n < rBoxes.size(); ++n)
    {
        SwTableBox* pSelBox = rBoxes[n];
        OSL_ENSURE( pSelBox, "Box is not within the Table" );

        // Insert nCnt new Lines into the Box
        SwTableLine* pInsLine = pSelBox->GetUpper();
        SwTableBoxFormat* pFrameFormat = pSelBox->GetFrameFormat();

        // Respect the Line's height, reset if needed
        SwFormatFrameSize aFSz( pInsLine->GetFrameFormat()->GetFrameSize() );
        if ( bSameHeight && SwFrameSize::Variable == aFSz.GetHeightSizeType() )
            aFSz.SetHeightSizeType( SwFrameSize::Minimum );

        bool bChgLineSz = 0 != aFSz.GetHeight() || bSameHeight;
        if ( bChgLineSz )
            aFSz.SetHeight( ( bSameHeight ? pRowHeights[ n ] : aFSz.GetHeight() ) /
                             (nCnt + 1) );

        SwTableBox* pNewBox = new SwTableBox( pFrameFormat, nCnt, pInsLine );
        sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox );
        pInsLine->GetTabBoxes()[nBoxPos] = pNewBox; // overwrite old one

        // Delete background/border attribute
        SwTableBox* pLastBox = pSelBox;         // To distribute the TextNodes!
        // If Areas are contained in the Box, it stays as is
        // !! If this is changed we need to adapt the Undo, too !!!
        bool bMoveNodes = true;
        {
            SwNodeOffset nSttNd = pLastBox->GetSttIdx() + 1,
                    nEndNd = pLastBox->GetSttNd()->EndOfSectionIndex();
            while( nSttNd < nEndNd )
                if( !rDoc.GetNodes()[ nSttNd++ ]->IsTextNode() )
                {
                    bMoveNodes = false;
                    break;
                }
        }

        SwTableBoxFormat* pCpyBoxFrameFormat = pSelBox->GetFrameFormat();
        bool bChkBorder = nullptr != pCpyBoxFrameFormat->GetBox().GetTop();
        if( bChkBorder )
            pCpyBoxFrameFormat = pSelBox->ClaimFrameFormat();

        for( sal_uInt16 i = 0; i <= nCnt; ++i )
        {
            // Create a new Line in the new Box
            SwTableLine* pNewLine = new SwTableLine(
                    pInsLine->GetFrameFormat(), 1, pNewBox );
            if( bChgLineSz )
            {
                pNewLine->ClaimFrameFormat()->SetFormatAttr( aFSz );
            }

            pNewBox->GetTabLines().insert( pNewBox->GetTabLines().begin() + i, pNewLine );
            // then a new Box in the Line
            if( !i )        // hang up the original Box
            {
                pSelBox->SetUpper( pNewLine );
                pNewLine->GetTabBoxes().insert( pNewLine->GetTabBoxes().begin(), pSelBox );
            }
            else
            {
                ::InsTableBox( rDoc, pTableNd, pNewLine, pCpyBoxFrameFormat,
                                pLastBox, 0 );

                if( bChkBorder )
                {
                    pCpyBoxFrameFormat = pNewLine->GetTabBoxes()[ 0 ]->ClaimFrameFormat();
                    SvxBoxItem aTmp( pCpyBoxFrameFormat->GetBox() );
                    aTmp.SetLine( nullptr, SvxBoxItemLine::TOP );
                    pCpyBoxFrameFormat->SetFormatAttr( aTmp );
                    bChkBorder = false;
                }

                if( bMoveNodes )
                {
                    const SwNode* pEndNd = pLastBox->GetSttNd()->EndOfSectionNode();
                    if( pLastBox->GetSttIdx()+SwNodeOffset(2) != pEndNd->GetIndex() )
                    {
                        // Move TextNodes
                        SwNodeRange aRg( *pLastBox->GetSttNd(), SwNodeOffset(+2), *pEndNd );
                        pLastBox = pNewLine->GetTabBoxes()[0];  // reset
                        SwNodeIndex aInsPos( *pLastBox->GetSttNd(), 1 );
                        rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aInsPos.GetNode(), false);
                        rDoc.GetNodes().Delete( aInsPos ); // delete the empty one
                    }
                }
            }
        }
        // In Boxes with Lines, we can only have Size/Fillorder
        pFrameFormat = pNewBox->ClaimFrameFormat();
        pFrameFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 );
        pFrameFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 );
    }

    pRowHeights.reset();

    GCLines();

    aFndBox.MakeFrames( *this );

#if defined DBG_UTIL
    CheckBoxWidth(GetTabLines(), *GetFrameFormat());;
    CheckTableLayout(GetTabLines());
#endif
    return true;
}

bool SwTable::SplitCol(SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt)
{
    OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" );
    SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
    if( !pTableNd )
        return false;

    // TL_CHART2: splitting/merging of a number of cells or rows will usually make
    // the table too complex to be handled with chart.
    // Thus we tell the charts to use their own data provider and forget about this table
    rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );

    SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());    // Delete HTML Layout
    SwSelBoxes aSelBoxes(rBoxes);
    ExpandSelection( aSelBoxes );

    // Find Lines for the Layout update
    FndBox_ aFndBox( nullptr, nullptr );
    aFndBox.SetTableLines( aSelBoxes, *this );
    aFndBox.DelFrames( *this );

    CpyTabFrames aFrameArr;
    std::vector<SwTableBoxFormat*> aLastBoxArr;
    for (size_t n = 0; n < aSelBoxes.size(); ++n)
    {
        SwTableBox* pSelBox = aSelBoxes[n];
        OSL_ENSURE( pSelBox, "Box is not in the table" );

        // We don't want to split small table cells into very very small cells
        if( pSelBox->GetFrameFormat()->GetFrameSize().GetWidth()/( nCnt + 1 ) < 10 )
            continue;

        // Then split the nCnt Box up into nCnt Boxes
        SwTableLine* pInsLine = pSelBox->GetUpper();
        sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox );

        // Find the Frame Format in the Frame Format Array
        SwTableBoxFormat* pLastBoxFormat;
        CpyTabFrame aFindFrame( pSelBox->GetFrameFormat() );
        CpyTabFrames::const_iterator itFind = aFrameArr.lower_bound( aFindFrame );
        const size_t nFndPos = itFind - aFrameArr.begin();
        if( itFind == aFrameArr.end() || !(*itFind == aFindFrame) )
        {
            // Change the FrameFormat
            aFindFrame.pNewFrameFormat = pSelBox->ClaimFrameFormat();
            SwTwips nBoxSz = aFindFrame.pNewFrameFormat->GetFrameSize().GetWidth();
            SwTwips nNewBoxSz = nBoxSz / ( nCnt + 1 );
            aFindFrame.pNewFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
                                                        nNewBoxSz, 0 ) );
            aFrameArr.insert( aFindFrame );

            pLastBoxFormat = aFindFrame.pNewFrameFormat;
            if( nBoxSz != ( nNewBoxSz * (nCnt + 1)))
            {
                // We have a remainder, so we need to define an own Format
                // for the last Box.
                pLastBoxFormat = new SwTableBoxFormat( *aFindFrame.pNewFrameFormat );
                pLastBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
                                nBoxSz - ( nNewBoxSz * nCnt ), 0 ) );
            }
            aLastBoxArr.insert( aLastBoxArr.begin() + nFndPos, pLastBoxFormat );
        }
        else
        {
            aFindFrame = aFrameArr[ nFndPos ];
            pSelBox->ChgFrameFormat( aFindFrame.pNewFrameFormat );
            pLastBoxFormat = aLastBoxArr[ nFndPos ];
        }

        // Insert the Boxes at the Position
        for( sal_uInt16 i = 1; i < nCnt; ++i )
            ::InsTableBox( rDoc, pTableNd, pInsLine, aFindFrame.pNewFrameFormat,
                        pSelBox, nBoxPos + i ); // insert after

        ::InsTableBox( rDoc, pTableNd, pInsLine, pLastBoxFormat,
                    pSelBox, nBoxPos + nCnt );  // insert after

        // Special treatment for the Border:
        const SvxBoxItem& aSelBoxItem = aFindFrame.pNewFrameFormat->GetBox();
        if( aSelBoxItem.GetRight() )
        {
            pInsLine->GetTabBoxes()[ nBoxPos + nCnt ]->ClaimFrameFormat();

            SvxBoxItem aTmp( aSelBoxItem );
            aTmp.SetLine( nullptr, SvxBoxItemLine::RIGHT );
            aFindFrame.pNewFrameFormat->SetFormatAttr( aTmp );

            // Remove the Format from the "cache"
            for( auto i = aFrameArr.size(); i; )
            {
                const CpyTabFrame& rCTF = aFrameArr[ --i ];
                if( rCTF.pNewFrameFormat == aFindFrame.pNewFrameFormat ||
                    rCTF.pFrameFormat == aFindFrame.pNewFrameFormat )
                {
                    aFrameArr.erase( aFrameArr.begin() + i );
                    aLastBoxArr.erase( aLastBoxArr.begin() + i );
                }
            }
        }
    }

    // Update Layout
    aFndBox.MakeFrames( *this );

#if defined DBG_UTIL
    CheckBoxWidth(GetTabLines(), *GetFrameFormat());;
    CheckTableLayout(GetTabLines());
#endif
    return true;
}

/*
 * >> MERGE <<
 * Algorithm:
 *    If we only have one Line in the FndBox_, take this Line and test
 *    the Box count:
 * If we have more than one Box, we merge on Box level, meaning
 *      the new Box will be as wide as the old ones.
 * All Lines that are above/under the Area, are inserted into
 *        the Box as Line + Box.
 * All Lines that come before/after the Area, are inserted into
 *          the Boxes Left/Right.
 *
 * >> MERGE <<
 */
static void lcl_CpyLines( sal_uInt16 nStt, sal_uInt16 nEnd,
                                SwTableLines& rLines,
                                SwTableBox* pInsBox,
                                sal_uInt16 nPos = USHRT_MAX )
{
    for( sal_uInt16 n = nStt; n < nEnd; ++n )
        rLines[n]->SetUpper( pInsBox );
    if( USHRT_MAX == nPos )
        nPos = pInsBox->GetTabLines().size();
    pInsBox->GetTabLines().insert( pInsBox->GetTabLines().begin() + nPos,
                             rLines.begin() + nStt, rLines.begin() + nEnd );
    rLines.erase( rLines.begin() + nStt, rLines.begin() + nEnd );
}

static void lcl_CpyBoxes( sal_uInt16 nStt, sal_uInt16 nEnd,
                                SwTableBoxes& rBoxes,
                                SwTableLine* pInsLine )
{
    for( sal_uInt16 n = nStt; n < nEnd; ++n )
        rBoxes[n]->SetUpper( pInsLine );
    sal_uInt16 nPos = pInsLine->GetTabBoxes().size();
    pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + nPos,
                              rBoxes.begin() + nStt, rBoxes.begin() + nEnd );
    rBoxes.erase( rBoxes.begin() + nStt, rBoxes.begin() + nEnd );
}

static void lcl_CalcWidth( SwTableBox* pBox )
{
    // Assertion: Every Line in the Box is as large
    SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
    OSL_ENSURE( pBox->GetTabLines().size(), "Box does not have any Lines" );

    SwTableLine* pLine = pBox->GetTabLines()[0];
    OSL_ENSURE( pLine, "Box is not within a Line" );

    tools::Long nWidth = 0;
    for( auto pTabBox : pLine->GetTabBoxes() )
        nWidth += pTabBox->GetFrameFormat()->GetFrameSize().GetWidth();

    pFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 ));

    // Boxes with Lines can only have Size/Fillorder
    pFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 );
    pFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 );
}

namespace {

struct InsULPara
{
    SwTableNode* pTableNd;
    SwTableLine* pInsLine;
    SwTableBox* pInsBox;
    bool bUL_LR : 1;        // Upper-Lower(true) or Left-Right(false) ?
    bool bUL : 1;           // Upper-Left(true) or Lower-Right(false) ?

    SwTableBox* pLeftBox;

    InsULPara( SwTableNode* pTNd,
                SwTableBox* pLeft,
                SwTableLine* pLine )
        : pTableNd( pTNd ), pInsLine( pLine ), pInsBox( nullptr ),
        pLeftBox( pLeft )
        {   bUL_LR = true; bUL = true; }

    void SetLeft( SwTableBox* pBox )
        { bUL_LR = false;   bUL = true; if( pBox ) pInsBox = pBox; }
    void SetRight( SwTableBox* pBox )
        { bUL_LR = false;   bUL = false; if( pBox ) pInsBox = pBox; }
    void SetLower( SwTableLine* pLine )
        { bUL_LR = true;    bUL = false; if( pLine ) pInsLine = pLine; }
};

}

static void lcl_Merge_MoveLine(FndLine_ & rFndLine, InsULPara *const pULPara);

static void lcl_Merge_MoveBox(FndBox_ & rFndBox, InsULPara *const pULPara)
{
    SwTableBoxes* pBoxes;

    sal_uInt16 nStt = 0, nEnd = rFndBox.GetLines().size();
    sal_uInt16 nInsPos = USHRT_MAX;
    if( !pULPara->bUL_LR )  // Left/Right
    {
        sal_uInt16 nPos;
        SwTableBox* pFndTableBox = rFndBox.GetBox();
        pBoxes = &pFndTableBox->GetUpper()->GetTabBoxes();
        if( pULPara->bUL )  // Left ?
        {
            // if there are Boxes before it, move them
            nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox );
            if( 0 != nPos )
                lcl_CpyBoxes( 0, nPos, *pBoxes, pULPara->pInsLine );
        }
        else                // Right
        {
            // if there are Boxes behind it, move them
            nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox );
            if( nPos +1 < o3tl::narrowing<sal_uInt16>(pBoxes->size()) )
            {
                nInsPos = pULPara->pInsLine->GetTabBoxes().size();
                lcl_CpyBoxes( nPos+1, pBoxes->size(),
                                    *pBoxes, pULPara->pInsLine );
            }
        }
    }
    // Upper/Lower and still deeper?
    else if (!rFndBox.GetLines().empty())
    {
        // Only search the Line from which we need to move
        nStt = pULPara->bUL ? 0 : rFndBox.GetLines().size()-1;
        nEnd = nStt+1;
    }

    pBoxes = &pULPara->pInsLine->GetTabBoxes();

    // Is there still a level to step down to?
    if (rFndBox.GetBox()->GetTabLines().empty())
        return;

    SwTableBox* pBox = new SwTableBox(
            rFndBox.GetBox()->GetFrameFormat(),
            0, pULPara->pInsLine );
    InsULPara aPara( *pULPara );
    aPara.pInsBox = pBox;
    for (FndLines_t::iterator it = rFndBox.GetLines().begin() + nStt;
         it != rFndBox.GetLines().begin() + nEnd; ++it )
    {
        lcl_Merge_MoveLine(**it, &aPara);
    }
    if( !pBox->GetTabLines().empty() )
    {
        if( USHRT_MAX == nInsPos )
            nInsPos = pBoxes->size();
        pBoxes->insert( pBoxes->begin() + nInsPos, pBox );
        lcl_CalcWidth( pBox );      // calculate the Box's width
    }
    else
        delete pBox;
}

static void lcl_Merge_MoveLine(FndLine_& rFndLine, InsULPara *const pULPara)
{
    SwTableLines* pLines;

    sal_uInt16 nStt = 0, nEnd = rFndLine.GetBoxes().size();
    sal_uInt16 nInsPos = USHRT_MAX;
    if( pULPara->bUL_LR )   // UpperLower ?
    {
        sal_uInt16 nPos;
        SwTableLine* pFndLn = rFndLine.GetLine();
        pLines = pFndLn->GetUpper() ?
                        &pFndLn->GetUpper()->GetTabLines() :
                        &pULPara->pTableNd->GetTable().GetTabLines();

        SwTableBox* pLBx = rFndLine.GetBoxes().front()->GetBox();
        SwTableBox* pRBx = rFndLine.GetBoxes().back()->GetBox();
        sal_uInt16 nLeft = pFndLn->GetBoxPos( pLBx );
        sal_uInt16 nRight = pFndLn->GetBoxPos( pRBx );

        if( !nLeft || nRight == pFndLn->GetTabBoxes().size() )
        {
            if( pULPara->bUL )  // Upper ?
            {
                // If there are Lines before it, move them
                nPos = pLines->GetPos( pFndLn );
                if( 0 != nPos )
                    lcl_CpyLines( 0, nPos, *pLines, pULPara->pInsBox );
            }
            else
                // If there are Lines after it, move them
                if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) )
                {
                    nInsPos = pULPara->pInsBox->GetTabLines().size();
                    lcl_CpyLines( nPos+1, pLines->size(), *pLines,
                                        pULPara->pInsBox );
                }
        }
        else
        {
            // There are still Boxes on the left side, so put the Left-
            // and Merge-Box into one Box and Line, insert before/after
            // a Line with a Box, into which the upper/lower Lines are
            // inserted
            SwTableLine* pInsLine = pULPara->pLeftBox->GetUpper();
            SwTableBox* pLMBox = new SwTableBox(
                pULPara->pLeftBox->GetFrameFormat(), 0, pInsLine );
            SwTableLine* pLMLn = new SwTableLine(
                        pInsLine->GetFrameFormat(), 2, pLMBox );
            pLMLn->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE );

            pLMBox->GetTabLines().insert( pLMBox->GetTabLines().begin(), pLMLn );

            lcl_CpyBoxes( 0, 2, pInsLine->GetTabBoxes(), pLMLn );

            pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLMBox );

            if( pULPara->bUL )  // Upper ?
            {
                // If there are Lines before it, move them
                nPos = pLines->GetPos( pFndLn );
                if( 0 != nPos )
                    lcl_CpyLines( 0, nPos, *pLines, pLMBox, 0 );
            }
            else
                // If there are Lines after it, move them
                if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) )
                    lcl_CpyLines( nPos+1, pLines->size(), *pLines,
                                        pLMBox );
            lcl_CalcWidth( pLMBox );        // calculate the Box's width
        }
    }
    // Left/Right
    else
    {
        // Find only the Line from which we need to move
        nStt = pULPara->bUL ? 0 : rFndLine.GetBoxes().size()-1;
        nEnd = nStt+1;
    }
    pLines = &pULPara->pInsBox->GetTabLines();

    SwTableLine* pNewLine = new SwTableLine(
        rFndLine.GetLine()->GetFrameFormat(), 0, pULPara->pInsBox );
    InsULPara aPara( *pULPara );       // copying
    aPara.pInsLine = pNewLine;
    FndBoxes_t & rLineBoxes = rFndLine.GetBoxes();
    for (FndBoxes_t::iterator it = rLineBoxes.begin() + nStt;
         it != rLineBoxes.begin() + nEnd; ++it)
    {
        lcl_Merge_MoveBox(**it, &aPara);
    }

    if( !pNewLine->GetTabBoxes().empty() )
    {
        if( USHRT_MAX == nInsPos )
            nInsPos = pLines->size();
        pLines->insert( pLines->begin() + nInsPos, pNewLine );
    }
    else
        delete pNewLine;
}

static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox );

bool SwTable::OldMerge( SwDoc& rDoc, const SwSelBoxes& rBoxes,
                        SwTableBox* pMergeBox, SwUndoTableMerge* pUndo )
{
    OSL_ENSURE( !rBoxes.empty() && pMergeBox, "no valid values" );
    SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
    if( !pTableNd )
        return false;

    // Find all Boxes/Lines
    FndBox_ aFndBox( nullptr, nullptr );
    {
        FndPara aPara( rBoxes, &aFndBox );
        ForEach_FndLineCopyCol( GetTabLines(), &aPara );
    }
    if( aFndBox.GetLines().empty() )
        return false;

    // TL_CHART2: splitting/merging of a number of cells or rows will usually make
    // the table too complex to be handled with chart.
    // Thus we tell the charts to use their own data provider and forget about this table
    rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );

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

    if( pUndo )
        pUndo->SetSelBoxes( rBoxes );

    // Find Lines for the Layout update
    aFndBox.SetTableLines( *this );
    aFndBox.DelFrames( *this );

    FndBox_* pFndBox = &aFndBox;
    while( 1 == pFndBox->GetLines().size() &&
            1 == pFndBox->GetLines().front()->GetBoxes().size() )
    {
        pFndBox = pFndBox->GetLines().front()->GetBoxes().front().get();
    }

    SwTableLine* pInsLine = new SwTableLine(
                pFndBox->GetLines().front()->GetLine()->GetFrameFormat(), 0,
                !pFndBox->GetUpper() ? nullptr : pFndBox->GetBox() );
    pInsLine->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE );

    // Add the new Line
    SwTableLines* pLines =  pFndBox->GetUpper() ?
                  &pFndBox->GetBox()->GetTabLines() :  &GetTabLines();

    SwTableLine* pNewLine = pFndBox->GetLines().front()->GetLine();
    sal_uInt16 nInsPos = pLines->GetPos( pNewLine );
    pLines->insert( pLines->begin() + nInsPos, pInsLine );

    SwTableBox* pLeftBox = new SwTableBox( pMergeBox->GetFrameFormat(), 0, pInsLine );
    SwTableBox* pRightBox = new SwTableBox( pMergeBox->GetFrameFormat(), 0, pInsLine );
    pMergeBox->SetUpper( pInsLine );
    pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLeftBox );
    pLeftBox->ClaimFrameFormat();
    pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 1, pMergeBox);
    pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 2, pRightBox );
    pRightBox->ClaimFrameFormat();

    // This contains all Lines that are above the selected Area,
    // thus they form a Upper/Lower Line
    InsULPara aPara( pTableNd, pLeftBox, pInsLine );

    // Move the overlapping upper/lower Lines of the selected Area
    for (auto & it : pFndBox->GetLines().front()->GetBoxes())
    {
        lcl_Merge_MoveBox(*it, &aPara);
    }
    aPara.SetLower( pInsLine );
    const auto nEnd = pFndBox->GetLines().size()-1;
    for (auto & it : pFndBox->GetLines()[nEnd]->GetBoxes())
    {
        lcl_Merge_MoveBox(*it, &aPara);
    }

    // Move the Boxes extending into the selected Area from left/right
    aPara.SetLeft( pLeftBox );
    for (auto & rpFndLine : pFndBox->GetLines())
    {
        lcl_Merge_MoveLine( *rpFndLine, &aPara );
    }

    aPara.SetRight( pRightBox );
    for (auto & rpFndLine : pFndBox->GetLines())
    {
        lcl_Merge_MoveLine( *rpFndLine, &aPara );
    }

    if( pLeftBox->GetTabLines().empty() )
        DeleteBox_( *this, pLeftBox, nullptr, false, false );
    else
    {
        lcl_CalcWidth( pLeftBox );      // calculate the Box's width
        if( pUndo && pLeftBox->GetSttNd() )
            pUndo->AddNewBox( pLeftBox->GetSttIdx() );
    }
    if( pRightBox->GetTabLines().empty() )
        DeleteBox_( *this, pRightBox, nullptr, false, false );
    else
    {
        lcl_CalcWidth( pRightBox );     // calculate the Box's width
        if( pUndo && pRightBox->GetSttNd() )
            pUndo->AddNewBox( pRightBox->GetSttIdx() );
    }

    DeleteSel( rDoc, rBoxes, nullptr, nullptr, false, false );

    // Clean up this Line's structure once again, generally all of them
    GCLines();

    for( const auto& rpBox : GetTabLines()[0]->GetTabBoxes() )
        lcl_BoxSetHeadCondColl(rpBox);

    aFndBox.MakeFrames( *this );

#if defined DBG_UTIL
    CheckBoxWidth(GetTabLines(), *GetFrameFormat());;
    CheckTableLayout(GetTabLines());
#endif

    return true;
}

static void lcl_CheckRowSpan( SwTable &rTable )
{
    const tools::Long nLineCount = static_cast<tools::Long>(rTable.GetTabLines().size());
    tools::Long nMaxSpan = nLineCount;
    tools::Long nMinSpan = 1;
    while( nMaxSpan )
    {
        SwTableLine* pLine = rTable.GetTabLines()[ nLineCount - nMaxSpan ];
        for( auto pBox : pLine->GetTabBoxes() )
        {
            sal_Int32 nRowSpan = pBox->getRowSpan();
            if( nRowSpan > nMaxSpan )
                pBox->setRowSpan( nMaxSpan );
            else if( nRowSpan < nMinSpan )
                pBox->setRowSpan( nMinSpan > 0 ? nMaxSpan : nMinSpan );
        }
        --nMaxSpan;
        nMinSpan = -nMaxSpan;
    }
}

static sal_uInt16 lcl_GetBoxOffset( const FndBox_& rBox )
{
    // Find the first Box
    const FndBox_* pFirstBox = &rBox;
    while (!pFirstBox->GetLines().empty())
    {
        pFirstBox = pFirstBox->GetLines().front()->GetBoxes().front().get();
    }

    sal_uInt16 nRet = 0;
    // Calculate the position relative to above via the Lines
    const SwTableBox* pBox = pFirstBox->GetBox();
    do {
        const SwTableBoxes& rBoxes = pBox->GetUpper()->GetTabBoxes();
        for( auto pCmp : rBoxes )
        {
            if (pBox==pCmp)
                break;
            nRet = nRet + o3tl::narrowing<sal_uInt16>(pCmp->GetFrameFormat()->GetFrameSize().GetWidth());
        }
        pBox = pBox->GetUpper()->GetUpper();
    } while( pBox );
    return nRet;
}

static sal_uInt16 lcl_GetLineWidth( const FndLine_& rLine )
{
    sal_uInt16 nRet = 0;
    for( auto n = rLine.GetBoxes().size(); n; )
    {
        nRet = nRet + o3tl::narrowing<sal_uInt16>(rLine.GetBoxes()[--n]->GetBox()
                            ->GetFrameFormat()->GetFrameSize().GetWidth());
    }
    return nRet;
}

static void lcl_CalcNewWidths(const FndLines_t& rFndLines, CpyPara& rPara)
{
    rPara.pWidths.reset();
    const size_t nLineCount = rFndLines.size();
    if( nLineCount )
    {
        rPara.pWidths = std::make_shared< std::vector< std::vector< sal_uLong > > >
                        ( nLineCount );
        // First we collect information about the left/right borders of all
        // selected cells
        for( size_t nLine = 0; nLine < nLineCount; ++nLine )
        {
            std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ];
            const FndLine_ *pFndLine = rFndLines[ nLine ].get();
            if( pFndLine && !pFndLine->GetBoxes().empty() )
            {
                const SwTableLine *pLine = pFndLine->GetLine();
                if( pLine && !pLine->GetTabBoxes().empty() )
                {
                    size_t nBoxCount = pLine->GetTabBoxes().size();
                    sal_uLong nPos = 0;
                    // The first selected box...
                    const SwTableBox *const pSel =
                        pFndLine->GetBoxes().front()->GetBox();
                    size_t nBox = 0;
                    // Sum up the width of all boxes before the first selected box
                    while( nBox < nBoxCount )
                    {
                        SwTableBox* pBox = pLine->GetTabBoxes()[nBox++];
                        if( pBox != pSel )
                            nPos += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
                        else
                            break;
                    }
                    // nPos is now the left border of the first selected box
                    if( rPara.nMinLeft > nPos )
                        rPara.nMinLeft = nPos;
                    nBoxCount = pFndLine->GetBoxes().size();
                    rWidth = std::vector< sal_uLong >( nBoxCount+2 );
                    rWidth[ 0 ] = nPos;
                    // Add now the widths of all selected boxes and store
                    // the positions in the vector
                    for( nBox = 0; nBox < nBoxCount; )
                    {
                        nPos += pFndLine->GetBoxes()[nBox]
                            ->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
                        rWidth[ ++nBox ] = nPos;
                    }
                    // nPos: The right border of the last selected box
                    if( rPara.nMaxRight < nPos )
                        rPara.nMaxRight = nPos;
                    if( nPos <= rWidth[ 0 ] )
                        rWidth.clear();
                }
            }
        }
    }
    // Second step: calculate the new widths for the copied cells
    sal_uLong nSelSize = rPara.nMaxRight - rPara.nMinLeft;
    if( !nSelSize )
        return;

    for( size_t nLine = 0; nLine < nLineCount; ++nLine )
    {
        std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ];
        const size_t nCount = rWidth.size();
        if( nCount > 2 )
        {
            rWidth[ nCount - 1 ] = rPara.nMaxRight;
            sal_uLong nLastPos = 0;
            for( size_t nBox = 0; nBox < nCount; ++nBox )
            {
                sal_uInt64 nNextPos = rWidth[ nBox ];
                nNextPos -= rPara.nMinLeft;
                nNextPos *= rPara.nNewSize;
                nNextPos /= nSelSize;
                rWidth[ nBox ] = static_cast<sal_uLong>(nNextPos - nLastPos);
                nLastPos = static_cast<sal_uLong>(nNextPos);
            }
        }
    }
}

static void
lcl_CopyLineToDoc(FndLine_ const& rpFndLn, CpyPara *const pCpyPara);

static void lcl_CopyBoxToDoc(FndBox_ const& rFndBox, CpyPara *const pCpyPara)
{
    // Calculation of new size
    sal_uLong nRealSize;
    sal_uLong nDummy1 = 0;
    sal_uLong nDummy2 = 0;
    if( pCpyPara->pTableNd->GetTable().IsNewModel() )
    {
        if( pCpyPara->nBoxIdx == 1 )
            nDummy1 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][0];
        nRealSize = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx++];
        if( pCpyPara->nBoxIdx == (*pCpyPara->pWidths)[pCpyPara->nLnIdx].size()-1 )
            nDummy2 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx];
    }
    else
    {
        nRealSize = pCpyPara->nNewSize;
        nRealSize *= rFndBox.GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
        if (pCpyPara->nOldSize == 0)
            throw o3tl::divide_by_zero();
        nRealSize /= pCpyPara->nOldSize;
    }

    sal_uLong nSize;
    bool bDummy = nDummy1 > 0;
    if( bDummy )
        nSize = nDummy1;
    else
    {
        nSize = nRealSize;
        nRealSize = 0;
    }
    do
    {
        // Find the Frame Format in the list of all Frame Formats
        CpyTabFrame aFindFrame(rFndBox.GetBox()->GetFrameFormat());

        std::shared_ptr<SwFormatFrameSize> aFrameSz(std::make_shared<SwFormatFrameSize>());
        CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame );
        const CpyTabFrames::size_type nFndPos = itFind - pCpyPara->rTabFrameArr.begin();

        // It *is* sometimes cool to have multiple tests/if's and assignments
        // in a single statement, and it is technically possible. But it is definitely
        // not simply readable - where from my POV reading code is done 1000 times
        // more often than writing it. Thus I dismantled the expression in smaller
        // chunks to keep it handy/understandable/changeable (hopefully without error)
        // The original for reference:
        // if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) ||
        //     ( aFrameSz = ( aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ]).pNewFrameFormat->
        //         GetFrameSize()).GetWidth() != static_cast<SwTwips>(nSize) )

        bool DoCopyIt(itFind == pCpyPara->rTabFrameArr.end());

        if(!DoCopyIt)
        {
            DoCopyIt = !(*itFind == aFindFrame);
        }

        if(!DoCopyIt)
        {
            aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ];
            aFrameSz.reset(aFindFrame.pNewFrameFormat->GetFrameSize().Clone());
            DoCopyIt = aFrameSz->GetWidth() != static_cast<SwTwips>(nSize);
        }

        if(DoCopyIt)
        {
            // It doesn't exist yet, so copy it
            aFindFrame.pNewFrameFormat = pCpyPara->rDoc.MakeTableBoxFormat();
            aFindFrame.pNewFrameFormat->CopyAttrs( *rFndBox.GetBox()->GetFrameFormat() );
            if( !pCpyPara->bCpyContent )
                aFindFrame.pNewFrameFormat->ResetFormatAttr(  RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
            aFrameSz->SetWidth( nSize );
            aFindFrame.pNewFrameFormat->SetFormatAttr( *aFrameSz );
            pCpyPara->rTabFrameArr.insert( aFindFrame );
        }

        SwTableBox* pBox;
        if (!rFndBox.GetLines().empty())
        {
            pBox = new SwTableBox( aFindFrame.pNewFrameFormat,
                        rFndBox.GetLines().size(), pCpyPara->pInsLine );
            pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox );
            CpyPara aPara( *pCpyPara, pBox );
            aPara.nNewSize = nSize;     // get the size
            for (auto const& rpFndLine : rFndBox.GetLines())
            {
                lcl_CopyLineToDoc( *rpFndLine, &aPara );
            }
        }
        else
        {
            // Create an empty Box
            pCpyPara->rDoc.GetNodes().InsBoxen( pCpyPara->pTableNd, pCpyPara->pInsLine,
                            aFindFrame.pNewFrameFormat,
                            pCpyPara->rDoc.GetDfltTextFormatColl(),
                            nullptr, pCpyPara->nInsPos );
            pBox = pCpyPara->pInsLine->GetTabBoxes()[ pCpyPara->nInsPos ];
            if( bDummy )
                pBox->setDummyFlag( true );
            else if( pCpyPara->bCpyContent )
            {
                // Copy the content into this empty Box
                pBox->setRowSpan(rFndBox.GetBox()->getRowSpan());

                // We can also copy formulas and values, if we copy the content
                {
                    SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxAttrSet( pCpyPara->rDoc.GetAttrPool() );
                    aBoxAttrSet.Put(rFndBox.GetBox()->GetFrameFormat()->GetAttrSet());
                    if( aBoxAttrSet.Count() )
                    {
                        const SwTableBoxNumFormat* pItem;
                        SvNumberFormatter* pN = pCpyPara->rDoc.GetNumberFormatter( false );
                        if( pN && pN->HasMergeFormatTable() && (pItem = aBoxAttrSet.
                            GetItemIfSet( RES_BOXATR_FORMAT, false )) )
                        {
                            sal_uLong nOldIdx = pItem->GetValue();
                            sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
                            if( nNewIdx != nOldIdx )
                                aBoxAttrSet.Put( SwTableBoxNumFormat( nNewIdx ));
                        }
                        pBox->ClaimFrameFormat()->SetFormatAttr( aBoxAttrSet );
                    }
                }
                SwDoc& rFromDoc = rFndBox.GetBox()->GetFrameFormat()->GetDoc();
                SwNodeRange aCpyRg( *rFndBox.GetBox()->GetSttNd(), SwNodeOffset(1),
                        *rFndBox.GetBox()->GetSttNd()->EndOfSectionNode() );
                SwNodeIndex aInsIdx( *pBox->GetSttNd(), 1 );

                rFromDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aCpyRg, aInsIdx.GetNode(), nullptr, false);
                // Delete the initial TextNode
                pCpyPara->rDoc.GetNodes().Delete( aInsIdx );
            }
            ++pCpyPara->nInsPos;
        }
        if( nRealSize )
        {
            bDummy = false;
            nSize = nRealSize;
            nRealSize = 0;
        }
        else
        {
            bDummy = true;
            nSize = nDummy2;
            nDummy2 = 0;
        }
    }
    while( nSize );
}

static void
lcl_CopyLineToDoc(const FndLine_& rFndLine, CpyPara *const pCpyPara)
{
    // Find the Frame Format in the list of all Frame Formats
    CpyTabFrame aFindFrame( rFndLine.GetLine()->GetFrameFormat() );
    CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame );
    if( itFind == pCpyPara->rTabFrameArr.end() )
    {
        // It doesn't exist yet, so copy it
        aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pCpyPara->rDoc.MakeTableLineFormat());
        aFindFrame.pNewFrameFormat->CopyAttrs( *rFndLine.GetLine()->GetFrameFormat() );
        pCpyPara->rTabFrameArr.insert( aFindFrame );
    }
    else
        aFindFrame = *itFind;

    SwTableLine* pNewLine = new SwTableLine( reinterpret_cast<SwTableLineFormat*>(aFindFrame.pNewFrameFormat),
                        rFndLine.GetBoxes().size(), pCpyPara->pInsBox );
    if( pCpyPara->pInsBox )
    {
        SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines();
        rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
    }
    else
    {
        SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines();
        rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine);
    }

    CpyPara aPara( *pCpyPara, pNewLine );

    if( pCpyPara->pTableNd->GetTable().IsNewModel() )
    {
        aPara.nOldSize = 0; // will not be used
        aPara.nBoxIdx = 1;
    }
    else if( rFndLine.GetBoxes().size() ==
                    rFndLine.GetLine()->GetTabBoxes().size() )
    {
        // Get the Parent's size
        const SwFrameFormat* pFormat;

        if( rFndLine.GetLine()->GetUpper() )
            pFormat = rFndLine.GetLine()->GetUpper()->GetFrameFormat();
        else
            pFormat = pCpyPara->pTableNd->GetTable().GetFrameFormat();
        aPara.nOldSize = pFormat->GetFrameSize().GetWidth();
    }
    else
        // Calculate it
        for (auto &rpBox : rFndLine.GetBoxes())
        {
            aPara.nOldSize += rpBox->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
        }

    const FndBoxes_t& rBoxes = rFndLine.GetBoxes();
    for (auto const& it : rBoxes)
    {
        lcl_CopyBoxToDoc(*it, &aPara);
    }
    if( pCpyPara->pTableNd->GetTable().IsNewModel() )
        ++pCpyPara->nLnIdx;
}

void SwTable::CopyHeadlineIntoTable( SwTableNode& rTableNd )
{
    // Find all Boxes/Lines
    SwSelBoxes aSelBoxes;
    SwTableBox* pBox = GetTabSortBoxes()[ 0 ];
    pBox = GetTableBox( pBox->GetSttNd()->StartOfSectionNode()->GetIndex() + 1 );
    SelLineFromBox( pBox, aSelBoxes );

    FndBox_ aFndBox( nullptr, nullptr );
    {
        FndPara aPara( aSelBoxes, &aFndBox );
        ForEach_FndLineCopyCol( GetTabLines(), &aPara );
    }
    if( aFndBox.GetLines().empty() )
        return;

    SwitchFormulasToRelativeRepresentation();

    CpyTabFrames aCpyFormat;
    CpyPara aPara( &rTableNd, 1, aCpyFormat );
    aPara.nNewSize = aPara.nOldSize = rTableNd.GetTable().GetFrameFormat()->GetFrameSize().GetWidth();
    // Copy
    if( IsNewModel() )
        lcl_CalcNewWidths( aFndBox.GetLines(), aPara );
    for (const auto & rpFndLine : aFndBox.GetLines())
    {
         lcl_CopyLineToDoc( *rpFndLine, &aPara );
    }
    if( rTableNd.GetTable().IsNewModel() )
    {   // The copied line must not contain any row span attributes > 1
        SwTableLine* pLine = rTableNd.GetTable().GetTabLines()[0];
        OSL_ENSURE( !pLine->GetTabBoxes().empty(), "Empty Table Line" );
        for( auto pTableBox : pLine->GetTabBoxes() )
        {
            OSL_ENSURE( pTableBox, "Missing Table Box" );
            pTableBox->setRowSpan( 1 );
        }
    }
}

bool SwTable::MakeCopy( SwDoc& rInsDoc, const SwPosition& rPos,
                        const SwSelBoxes& rSelBoxes,
                        bool bCpyName, const TableStyleName& rStyleName ) const
{
    // Find all Boxes/Lines
    FndBox_ aFndBox( nullptr, nullptr );
    {
        FndPara aPara( rSelBoxes, &aFndBox );
        ForEach_FndLineCopyCol( const_cast<SwTableLines&>(GetTabLines()), &aPara );
    }
    if( aFndBox.GetLines().empty() )
        return false;

    // First copy the PoolTemplates for the Table, so that the Tables are
    // actually copied and have valid values.
    SwDoc& rSrcDoc = GetFrameFormat()->GetDoc();
    if( &rSrcDoc != &rInsDoc )
    {
        rInsDoc.CopyTextColl( *rSrcDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ) );
        rInsDoc.CopyTextColl( *rSrcDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN ) );
    }

    SwTable* pNewTable = const_cast<SwTable*>(rInsDoc.InsertTable(
            SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ),
            rPos, 1, 1, GetFrameFormat()->GetHoriOrient().GetHoriOrient(),
            nullptr, nullptr, false, IsNewModel() ));
    if( !pNewTable )
        return false;

    SwNodeIndex aIdx( rPos.GetNode(), -1 );
    SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
    ++aIdx;
    OSL_ENSURE( pTableNd, "Where is the TableNode now?" );

    pTableNd->GetTable().SetRowsToRepeat( GetRowsToRepeat() );

    pNewTable->SetTableStyleName(pTableNd->GetTable().GetTableStyleName());

    pTableNd->GetTable().SetTableStyleName(rStyleName);
    if( auto pSwDDETable = dynamic_cast<const SwDDETable*>(this) )
    {
        // A DDE-Table is being copied
        // Does the new Document actually have it's FieldType?
        SwFieldType* pFieldType = rInsDoc.getIDocumentFieldsAccess().InsertFieldType(
                                    *pSwDDETable->GetDDEFieldType() );
        OSL_ENSURE( pFieldType, "unknown FieldType" );

        // Change the Table Pointer at the Node
        pNewTable = new SwDDETable( *pNewTable,
                                 static_cast<SwDDEFieldType*>(pFieldType) );
        pTableNd->SetNewTable( std::unique_ptr<SwTable>(pNewTable), false );
    }

    pNewTable->GetFrameFormat()->CopyAttrs( *GetFrameFormat() );
    pNewTable->SetTableChgMode( GetTableChgMode() );

    // Destroy the already created Frames
    pTableNd->DelFrames();

    const_cast<SwTable*>(this)->SwitchFormulasToRelativeRepresentation();

    SwTableNumFormatMerge aTNFM(rSrcDoc, rInsDoc);

    // Also copy Names or enforce a new unique one
    if( bCpyName )
        pNewTable->GetFrameFormat()->SetFormatName( GetFrameFormat()->GetName() );

    CpyTabFrames aCpyFormat;
    CpyPara aPara( pTableNd, 1, aCpyFormat );
    aPara.nNewSize = aPara.nOldSize = GetFrameFormat()->GetFrameSize().GetWidth();

    if( IsNewModel() )
        lcl_CalcNewWidths( aFndBox.GetLines(), aPara );
    // Copy
    for (const auto & rpFndLine : aFndBox.GetLines())
    {
         lcl_CopyLineToDoc( *rpFndLine, &aPara );
    }

    // Set the "right" margin above/below
    {
        FndLine_* pFndLn = aFndBox.GetLines().front().get();
        SwTableLine* pLn = pFndLn->GetLine();
        const SwTableLine* pTmp = pLn;
        sal_uInt16 nLnPos = GetTabLines().GetPos( pTmp );
        if( USHRT_MAX != nLnPos && nLnPos )
        {
            // There is a Line before it
            SwCollectTableLineBoxes aLnPara( false, SplitTable_HeadlineOption::BorderCopy );

            pLn = GetTabLines()[ nLnPos - 1 ];
            for( const auto& rpBox : pLn->GetTabBoxes() )
                sw_Box_CollectBox( rpBox, &aLnPara );

            if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
                                lcl_GetLineWidth( *pFndLn )) )
            {
                aLnPara.SetValues( true );
                pLn = pNewTable->GetTabLines()[ 0 ];
                for( const auto& rpBox : pLn->GetTabBoxes() )
                    sw_BoxSetSplitBoxFormats(rpBox, &aLnPara );
            }
        }

        pFndLn = aFndBox.GetLines().back().get();
        pLn = pFndLn->GetLine();
        pTmp = pLn;
        nLnPos = GetTabLines().GetPos( pTmp );
        if( nLnPos < GetTabLines().size() - 1 )
        {
            // There is a Line following it
            SwCollectTableLineBoxes aLnPara( true, SplitTable_HeadlineOption::BorderCopy );

            pLn = GetTabLines()[ nLnPos + 1 ];
            for( const auto& rpBox : pLn->GetTabBoxes() )
                sw_Box_CollectBox( rpBox, &aLnPara );

            if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
                                lcl_GetLineWidth( *pFndLn )) )
            {
                aLnPara.SetValues( false );
                pLn = pNewTable->GetTabLines().back();
                for( const auto& rpBox : pLn->GetTabBoxes() )
                    sw_BoxSetSplitBoxFormats(rpBox, &aLnPara );
            }
        }
    }

    // We need to delete the initial Box
    DeleteBox_( *pNewTable, pNewTable->GetTabLines().back()->GetTabBoxes()[0],
                nullptr, false, false );

    if( pNewTable->IsNewModel() )
        lcl_CheckRowSpan( *pNewTable );
    // Clean up
    pNewTable->GCLines();

    pTableNd->MakeOwnFrames();  // re-generate the Frames

#if defined DBG_UTIL
    CheckTableLayout(GetTabLines());
#endif

    return true;
}

// Find the next Box with content from this Line
SwTableBox* SwTableLine::FindNextBox( const SwTable& rTable,
                     const SwTableBox* pSrchBox, bool bOvrTableLns ) const
{
    const SwTableLine* pLine = this;            // for M800
    SwTableBox* pBox;
    sal_uInt16 nFndPos;
    if( !GetTabBoxes().empty() && pSrchBox )
    {
        nFndPos = GetBoxPos( pSrchBox );
        if( USHRT_MAX != nFndPos &&
            nFndPos + 1 != o3tl::narrowing<sal_uInt16>(GetTabBoxes().size()) )
        {
            pBox = GetTabBoxes()[ nFndPos + 1 ];
            while( !pBox->GetTabLines().empty() )
                pBox = pBox->GetTabLines().front()->GetTabBoxes()[0];
            return pBox;
        }
    }

    if( GetUpper() )
    {
        nFndPos = GetUpper()->GetTabLines().GetPos( pLine );
        OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" );
        // Is there another Line?
        if( nFndPos+1 >= o3tl::narrowing<sal_uInt16>(GetUpper()->GetTabLines().size()) )
            return GetUpper()->GetUpper()->FindNextBox( rTable, GetUpper(), bOvrTableLns );
        pLine = GetUpper()->GetTabLines()[nFndPos+1];
    }
    else if( bOvrTableLns )       // Over a Table's the "BaseLines"??
    {
        // Search for the next Line in the Table
        nFndPos = rTable.GetTabLines().GetPos( pLine );
        if( nFndPos + 1 >= o3tl::narrowing<sal_uInt16>(rTable.GetTabLines().size()) )
            return nullptr;           // there are no more Boxes

        pLine = rTable.GetTabLines()[ nFndPos+1 ];
    }
    else
        return nullptr;

    if( !pLine->GetTabBoxes().empty() )
    {
        pBox = pLine->GetTabBoxes().front();
        while( !pBox->GetTabLines().empty() )
            pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
        return pBox;
    }
    return pLine->FindNextBox( rTable, nullptr, bOvrTableLns );
}

// Find the previous Box from this Line
SwTableBox* SwTableLine::FindPreviousBox( const SwTable& rTable,
                         const SwTableBox* pSrchBox, bool bOvrTableLns ) const
{
    const SwTableLine* pLine = this;            // for M800
    SwTableBox* pBox;
    sal_uInt16 nFndPos;
    if( !GetTabBoxes().empty() && pSrchBox )
    {
        nFndPos = GetBoxPos( pSrchBox );
        if( USHRT_MAX != nFndPos && nFndPos )
        {
            pBox = GetTabBoxes()[ nFndPos - 1 ];
            while( !pBox->GetTabLines().empty() )
            {
                pLine = pBox->GetTabLines().back();
                pBox = pLine->GetTabBoxes().back();
            }
            return pBox;
        }
    }

    if( GetUpper() )
    {
        nFndPos = GetUpper()->GetTabLines().GetPos( pLine );
        OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" );
        // Is there another Line?
        if( !nFndPos )
            return GetUpper()->GetUpper()->FindPreviousBox( rTable, GetUpper(), bOvrTableLns );
        pLine = GetUpper()->GetTabLines()[nFndPos-1];
    }
    else if( bOvrTableLns )       // Over a Table's the "BaseLines"??
    {
        // Search for the next Line in the Table
        nFndPos = rTable.GetTabLines().GetPos( pLine );
        if( !nFndPos )
            return nullptr;           // there are no more Boxes

        pLine = rTable.GetTabLines()[ nFndPos-1 ];
    }
    else
        return nullptr;

    if( !pLine->GetTabBoxes().empty() )
    {
        pBox = pLine->GetTabBoxes().back();
        while( !pBox->GetTabLines().empty() )
        {
            pLine = pBox->GetTabLines().back();
            pBox = pLine->GetTabBoxes().back();
        }
        return pBox;
    }
    return pLine->FindPreviousBox( rTable, nullptr, bOvrTableLns );
}

// Find the next Box with content from this Line
SwTableBox* SwTableBox::FindNextBox( const SwTable& rTable,
                         const SwTableBox* pSrchBox, bool bOvrTableLns ) const
{
    if( !pSrchBox  && GetTabLines().empty() )
        return const_cast<SwTableBox*>(this);
    return GetUpper()->FindNextBox( rTable, pSrchBox ? pSrchBox : this,
                                        bOvrTableLns );

}

// Find the next Box with content from this Line
SwTableBox* SwTableBox::FindPreviousBox( const SwTable& rTable,
                         const SwTableBox* pSrchBox ) const
{
    if( !pSrchBox && GetTabLines().empty() )
        return const_cast<SwTableBox*>(this);
    return GetUpper()->FindPreviousBox( rTable, pSrchBox ? pSrchBox : this );
}

static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox )
{
    // We need to adapt the paragraphs with conditional templates in the HeadLine
    const SwStartNode* pSttNd = pBox->GetSttNd();
    if( pSttNd )
        pSttNd->CheckSectionCondColl();
    else
        for( const SwTableLine* pLine : pBox->GetTabLines() )
            sw_LineSetHeadCondColl( pLine );
}

void sw_LineSetHeadCondColl( const SwTableLine* pLine )
{
    for( const SwTableBox* pBox : pLine->GetTabBoxes() )
        lcl_BoxSetHeadCondColl(pBox);
}

static SwTwips lcl_GetDistance( SwTableBox* pBox, bool bLeft )
{
    bool bFirst = true;
    SwTwips nRet = 0;
    SwTableLine* pLine;
    while( pBox )
    {
        pLine = pBox->GetUpper();
        if( !pLine )
            break;
        sal_uInt16 nStt = 0, nPos = pLine->GetBoxPos( pBox );

        if( bFirst && !bLeft )
            ++nPos;
        bFirst = false;

        while( nStt < nPos )
            nRet += pLine->GetTabBoxes()[ nStt++ ]->GetFrameFormat()
                            ->GetFrameSize().GetWidth();
        pBox = pLine->GetUpper();
    }
    return nRet;
}

static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
                         SwTwips nDist, bool bCheck )
{
    SwTableBoxes& rBoxes = pLine->GetTabBoxes();
    for( auto pBox : rBoxes )
    {
        SwFrameFormat* pFormat = pBox->GetFrameFormat();
        const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
        SwTwips nWidth = rSz.GetWidth();
        bool bGreaterBox = false;

        if( bCheck )
        {
            for( auto pLn : pBox->GetTabLines() )
                if( !::lcl_SetSelBoxWidth( pLn, rParam, nDist, true ))
                    return false;

            // Collect all "ContentBoxes"
            bGreaterBox = (TableChgMode::FixedWidthChangeAbs != rParam.nMode)
                       && ((nDist + (rParam.bLeft ? 0 : nWidth)) >= rParam.nSide);
            if (bGreaterBox
                || (!rParam.bBigger
                    && (std::abs(nDist + ((rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth) - rParam.nSide) < COLFUZZY)))
            {
                SwTwips nLowerDiff;
                if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode )
                {
                    // The "other Boxes" have been adapted, so change by this value
                    nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide;
                    nLowerDiff *= rParam.nDiff;
                    nLowerDiff /= rParam.nMaxSize;
                    nLowerDiff = rParam.nDiff - nLowerDiff;
                }
                else
                    nLowerDiff = rParam.nDiff;

                if( nWidth < nLowerDiff || nWidth - nLowerDiff < MINLAY )
                    return false;
            }
        }
        else
        {
            SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
            for( auto pLn : pBox->GetTabLines() )
            {
                rParam.nLowerDiff = 0;
                lcl_SetSelBoxWidth( pLn, rParam, nDist, false );

                if( nLowerDiff < rParam.nLowerDiff )
                    nLowerDiff = rParam.nLowerDiff;
            }
            rParam.nLowerDiff = nOldLower;

            if( nLowerDiff ||
                 (bGreaterBox = !nOldLower && TableChgMode::FixedWidthChangeAbs != rParam.nMode &&
                    ( nDist + ( rParam.bLeft ? 0 : nWidth ) ) >= rParam.nSide) ||
                ( std::abs( nDist + ( (rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth )
                            - rParam.nSide ) < COLFUZZY ))
            {
                // This column contains the Cursor - so decrease/increase
                SwFormatFrameSize aNew( rSz );

                if( !nLowerDiff )
                {
                    if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode )
                    {
                        // The "other Boxes" have been adapted, so change by this value
                        nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide;
                        nLowerDiff *= rParam.nDiff;
                        nLowerDiff /= rParam.nMaxSize;
                        nLowerDiff = rParam.nDiff - nLowerDiff;
                    }
                    else
                        nLowerDiff = rParam.nDiff;
                }

                rParam.nLowerDiff += nLowerDiff;

                if( rParam.bBigger )
                    aNew.SetWidth( nWidth + nLowerDiff );
                else
                    aNew.SetWidth( nWidth - nLowerDiff );
                rParam.aShareFormats.SetSize( *pBox, aNew );
                break;
            }
        }

        if( rParam.bLeft && rParam.nMode != TableChgMode::FixedWidthChangeAbs && nDist >= rParam.nSide )
            break;

        nDist += nWidth;

        // If it gets bigger, then that's it
        if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || !rParam.bLeft ) &&
                nDist >= rParam.nSide )
            break;
    }
    return true;
}

static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
                                SwTwips nDist, bool bCheck )
{
    SwTableBoxes& rBoxes = pLine->GetTabBoxes();
    for( auto pBox : rBoxes )
    {
        SwFrameFormat* pFormat = pBox->GetFrameFormat();
        const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
        SwTwips nWidth = rSz.GetWidth();

        if( bCheck )
        {
            for( auto pLn : pBox->GetTabLines() )
                if( !::lcl_SetOtherBoxWidth( pLn, rParam, nDist, true ))
                    return false;

            if( rParam.bBigger && ( TableChgMode::FixedWidthChangeAbs == rParam.nMode
                    ? std::abs( nDist - rParam.nSide ) < COLFUZZY
                    : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY
                                     : nDist >= rParam.nSide - COLFUZZY )) )
            {
                SwTwips nDiff;
                if( TableChgMode::FixedWidthChangeProp == rParam.nMode )        // Table fixed, proportional
                {
                    // calculate relative
                    nDiff = nWidth;
                    nDiff *= rParam.nDiff;
                    nDiff /= rParam.nMaxSize;
                }
                else
                    nDiff = rParam.nDiff;

                if( nWidth < nDiff || nWidth - nDiff < MINLAY )
                    return false;
            }
        }
        else
        {
            SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
            for( auto pLn : pBox->GetTabLines() )
            {
                rParam.nLowerDiff = 0;
                lcl_SetOtherBoxWidth( pLn, rParam, nDist, false );

                if( nLowerDiff < rParam.nLowerDiff )
                    nLowerDiff = rParam.nLowerDiff;
            }
            rParam.nLowerDiff = nOldLower;

            if( nLowerDiff ||
                ( TableChgMode::FixedWidthChangeAbs == rParam.nMode
                        ? std::abs( nDist - rParam.nSide ) < COLFUZZY
                        : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY
                                         : nDist >= rParam.nSide - COLFUZZY)
                 ) )
            {
                SwFormatFrameSize aNew( rSz );

                if( !nLowerDiff )
                {
                    if( TableChgMode::FixedWidthChangeProp == rParam.nMode )        // Table fixed, proportional
                    {
                        // calculate relative
                        nLowerDiff = nWidth;
                        nLowerDiff *= rParam.nDiff;
                        nLowerDiff /= rParam.nMaxSize;
                    }
                    else
                        nLowerDiff = rParam.nDiff;
                }

                rParam.nLowerDiff += nLowerDiff;

                if( rParam.bBigger )
                    aNew.SetWidth( nWidth - nLowerDiff );
                else
                    aNew.SetWidth( nWidth + nLowerDiff );

                rParam.aShareFormats.SetSize( *pBox, aNew );
            }
        }

        nDist += nWidth;
        if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || rParam.bLeft ) &&
            nDist > rParam.nSide )
            break;
    }
    return true;
}

static void lcl_AjustLines( SwTableLine* pLine, CR_SetBoxWidth& rParam )
{
    SwTableBoxes& rBoxes = pLine->GetTabBoxes();
    for( auto pBox : rBoxes )
    {
        SwFormatFrameSize aSz( pBox->GetFrameFormat()->GetFrameSize() );
        SwTwips nWidth = aSz.GetWidth();
        nWidth *= rParam.nDiff;
        nWidth /= rParam.nMaxSize;
        aSz.SetWidth( nWidth );
        rParam.aShareFormats.SetSize( *pBox, aSz );

        for( auto pLn : pBox->GetTabLines() )
            ::lcl_AjustLines( pLn, rParam );
    }
}

#if defined DBG_UTIL
void CheckTableLayout(const SwTableLines& rLines)
{
    for (auto pLn : rLines)
    {
        SwFrameFormat* pFormat = pLn->GetFrameFormat();
        SwIterator<SwRowFrame,SwFormat> aIter( *pFormat );
        for (SwRowFrame* pFrame=aIter.First(); pFrame; pFrame=aIter.Next())
        {
            if ( pFrame->GetTabLine() == pLn )
            {
                OSL_ENSURE( pFrame->GetUpper()->IsTabFrame(),
                            "Table layout does not match table structure" );
            }
        }
    }
}

SwTwips CheckBoxWidth(const SwTableLines& rLines, const SwFrameFormat& rFrameFormat )
{
    SwTwips nSize = rFrameFormat.GetFrameSize().GetWidth();
    for (auto pLn : rLines)
    {
        const SwTableBoxes& rBoxes = pLn->GetTabBoxes();

        SwTwips nCurrentSize = 0;
        // See if the tables have a correct width
        for (const SwTableBox* pBox : rBoxes)
        {
            nCurrentSize += CheckBoxWidth( pBox->GetTabLines(), *pBox->GetFrameFormat() );
        }

        if (sal::static_int_cast< tools::ULong >(std::abs(nCurrentSize - nSize)) >
            (COLFUZZY * rBoxes.size()))
        {
            OSL_FAIL( "Line's Boxes are too small or too large" );
        }
    }

    return nSize;
}
#endif

bool SwTable::SetColWidth( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
                        SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo )
{
    SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());    // Delete HTML Layout

    const SwFormatFrameSize& rSz = GetFrameFormat()->GetFrameSize();
    const SvxLRSpaceItem& rLR = GetFrameFormat()->GetLRSpace();

    bool bBigger,
        bRet = false,
        bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) ||
                TableChgWidthHeightType::CellLeft == extractPosition( eType );

    // Get the current Box's edge
    // Only needed for manipulating the width
    const SwTwips nDist = ::lcl_GetDistance( &rCurrentBox, bLeft );
    SwTwips nDistStt = 0;
    CR_SetBoxWidth aParam( eType, nRelDiff, nDist,
                            bLeft ? nDist : rSz.GetWidth() - nDist,
                            const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
    bBigger = aParam.bBigger;

    FN_lcl_SetBoxWidth fnSelBox, fnOtherBox;
    fnSelBox = lcl_SetSelBoxWidth;
    fnOtherBox = lcl_SetOtherBoxWidth;

    switch( extractPosition(eType) )
    {
    case TableChgWidthHeightType::ColRight:
    case TableChgWidthHeightType::ColLeft:
        if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
        {
            // First test if we have room at all
            bool bChgLRSpace = true;
            if( bBigger )
            {
                if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) &&
                    !rSz.GetWidthPercent() )
                {
                    // silence -Wsign-compare on Android with the static cast
                    bRet = rSz.GetWidth() < static_cast<unsigned short>(USHRT_MAX) - nRelDiff;
                    bChgLRSpace = bLeft ? rLR.ResolveLeft({}) >= nAbsDiff
                                        : rLR.ResolveRight({}) >= nAbsDiff;
                }
                else
                    bRet = bLeft ? rLR.ResolveLeft({}) >= nAbsDiff
                                 : rLR.ResolveRight({}) >= nAbsDiff;

                if( !bRet )
                {
                    // Then call itself recursively; only with another mode (proportional)
                    TableChgMode eOld = m_eTableChgMode;
                    m_eTableChgMode = TableChgMode::FixedWidthChangeProp;

                    bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff,
                                        ppUndo );
                    m_eTableChgMode = eOld;
                    return bRet;
                }
            }
            else
            {
                bRet = true;
                for( auto const & n: m_aLines )
                {
                    aParam.LoopClear();
                    if( !(*fnSelBox)( n, aParam, nDistStt, true ))
                    {
                        bRet = false;
                        break;
                    }
                }
            }

            if( bRet )
            {
                if( ppUndo )
                    ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));

                tools::Long nFrameWidth = LONG_MAX;
                LockModify();
                SwFormatFrameSize aSz( rSz );
                SvxLRSpaceItem aLR( rLR );
                if( bBigger )
                {
                    // If the Table does not have any room to grow, we need to create some!
                    // silence -Wsign-compare on Android with the static cast
                    if( aSz.GetWidth() + nRelDiff > static_cast<unsigned short>(USHRT_MAX) )
                    {
                        // Break down to USHRT_MAX / 2
                        CR_SetBoxWidth aTmpPara( TableChgWidthHeightType::ColLeft, aSz.GetWidth() / 2,
                                        0, aSz.GetWidth(), aParam.pTableNd );
                        for( size_t nLn = 0; nLn < m_aLines.size(); ++nLn )
                            ::lcl_AjustLines( m_aLines[ nLn ], aTmpPara );
                        aSz.SetWidth( aSz.GetWidth() / 2 );
                        aParam.nDiff = nRelDiff /= 2;
                        aParam.nSide /= 2;
                        aParam.nMaxSize /= 2;
                    }

                    if( bLeft )
                        aLR.SetLeft(
                            SvxIndentValue::twips(sal_uInt16(aLR.ResolveLeft({}) - nAbsDiff)));
                    else
                        aLR.SetRight(
                            SvxIndentValue::twips(sal_uInt16(aLR.ResolveRight({}) - nAbsDiff)));
                }
                else if( bLeft )
                    aLR.SetLeft(SvxIndentValue::twips(sal_uInt16(aLR.ResolveLeft({}) + nAbsDiff)));
                else
                    aLR.SetRight(
                        SvxIndentValue::twips(sal_uInt16(aLR.ResolveRight({}) + nAbsDiff)));

                if( bChgLRSpace )
                    GetFrameFormat()->SetFormatAttr( aLR );
                const SwFormatHoriOrient& rHOri = GetFrameFormat()->GetHoriOrient();
                if (text::HoriOrientation::FULL == rHOri.GetHoriOrient()
                    || (text::HoriOrientation::LEFT == rHOri.GetHoriOrient() && aLR.ResolveLeft({}))
                    || (text::HoriOrientation::RIGHT == rHOri.GetHoriOrient()
                        && aLR.ResolveRight({})))
                {
                    SwFormatHoriOrient aHOri( rHOri );
                    aHOri.SetHoriOrient( text::HoriOrientation::NONE );
                    GetFrameFormat()->SetFormatAttr( aHOri );

                    // If the Table happens to contain relative values (USHORT_MAX),
                    // we need to convert them to absolute ones now.
                    // Bug 61494
                    if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) &&
                        !rSz.GetWidthPercent() )
                    {
                        SwTabFrame* pTabFrame = SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First();
                        if( pTabFrame &&
                            pTabFrame->getFramePrintArea().Width() != rSz.GetWidth() )
                        {
                            nFrameWidth = pTabFrame->getFramePrintArea().Width();
                            if( bBigger )
                                nFrameWidth += nAbsDiff;
                            else
                                nFrameWidth -= nAbsDiff;
                        }
                    }
                }

                if( bBigger )
                    aSz.SetWidth( aSz.GetWidth() + nRelDiff );
                else
                    aSz.SetWidth( aSz.GetWidth() - nRelDiff );

                if (rSz.GetWidthPercent())
                    aSz.SetWidthPercent(static_cast<sal_uInt8>(
                        (aSz.GetWidth() * 100)
                        / (aSz.GetWidth() + aLR.ResolveRight({}) + aLR.ResolveLeft({}))));

                GetFrameFormat()->SetFormatAttr( aSz );

                UnlockModify();

                for( sal_uInt16 n = m_aLines.size(); n; )
                {
                    --n;
                    aParam.LoopClear();
                    (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
                }

                // If the Table happens to contain relative values (USHORT_MAX),
                // we need to convert them to absolute ones now.
                // Bug 61494
                if( LONG_MAX != nFrameWidth )
                {
                    SwFormatFrameSize aAbsSz(std::move(aSz));
                    aAbsSz.SetWidth( nFrameWidth );
                    GetFrameFormat()->SetFormatAttr( aAbsSz );
                }
            }
        }
        else if( bLeft ? nDist != 0 : std::abs( rSz.GetWidth() - nDist ) > COLFUZZY )
        {
            bRet = true;
            if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode )
                aParam.bBigger = !bBigger;

            // First test if we have room at all
            if( aParam.bBigger )
            {
                for( auto const & n: m_aLines )
                {
                    aParam.LoopClear();
                    if( !(*fnOtherBox)( n, aParam, 0, true ))
                    {
                        bRet = false;
                        break;
                    }
                }
            }
            else
            {
                for( auto const & n: m_aLines )
                {
                    aParam.LoopClear();
                    if( !(*fnSelBox)( n, aParam, nDistStt, true ))
                    {
                        bRet = false;
                        break;
                    }
                }
            }

            // If true, set it
            if( bRet )
            {
                CR_SetBoxWidth aParam1( aParam );
                if( ppUndo )
                    ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));

                if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft )
                {
                    for( sal_uInt16 n = m_aLines.size(); n; )
                    {
                        --n;
                        aParam.LoopClear();
                        aParam1.LoopClear();
                        (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
                        (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false );
                    }
                }
                else
                {
                    for( sal_uInt16 n = m_aLines.size(); n; )
                    {
                        --n;
                        aParam.LoopClear();
                        aParam1.LoopClear();
                        (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false );
                        (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
                    }
                }
            }
        }
        break;

    case TableChgWidthHeightType::CellRight:
    case TableChgWidthHeightType::CellLeft:
        if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
        {
            // Then call itself recursively; only with another mode (proportional)
            TableChgMode eOld = m_eTableChgMode;
            m_eTableChgMode = TableChgMode::FixedWidthChangeAbs;

            bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff,
                                ppUndo );
            m_eTableChgMode = eOld;
            return bRet;
        }
        else if( bLeft ? nDist != 0 : (rSz.GetWidth() - nDist) > COLFUZZY )
        {
            if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode )
                aParam.bBigger = !bBigger;

            // First, see if there is enough room at all
            SwTableBox* pBox = &rCurrentBox;
            SwTableLine* pLine = rCurrentBox.GetUpper();
            while( pLine->GetUpper() )
            {
                const SwTableBoxes::size_type nPos = pLine->GetBoxPos( pBox );
                if( bLeft ? nPos != 0 : nPos + 1 != pLine->GetTabBoxes().size() )
                    break;

                pBox = pLine->GetUpper();
                pLine = pBox->GetUpper();
            }

            if( pLine->GetUpper() )
            {
                // We need to correct the distance once again!
                aParam.nSide -= ::lcl_GetDistance( pLine->GetUpper(), true );

                if( bLeft )
                    aParam.nMaxSize = aParam.nSide;
                else
                    aParam.nMaxSize = pLine->GetUpper()->GetFrameFormat()->
                                    GetFrameSize().GetWidth() - aParam.nSide;
            }

            // First, see if there is enough room at all
            FN_lcl_SetBoxWidth fnTmp = aParam.bBigger ? fnOtherBox : fnSelBox;
            bRet = (*fnTmp)( pLine, aParam, nDistStt, true );

            // If true, set it
            if( bRet )
            {
                CR_SetBoxWidth aParam1( aParam );
                if( ppUndo )
                    ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));

                if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft )
                {
                    (*fnSelBox)( pLine, aParam, nDistStt, false );
                    (*fnOtherBox)( pLine, aParam1, nDistStt, false );
                }
                else
                {
                    (*fnOtherBox)( pLine, aParam1, nDistStt, false );
                    (*fnSelBox)( pLine, aParam, nDistStt, false );
                }
            }
        }
        break;
        default: break;
    }

#if defined DBG_UTIL
    if( bRet )
    {
        CheckBoxWidth(GetTabLines(), *GetFrameFormat());
        CheckTableLayout(GetTabLines());
    }
#endif

    return bRet;
}

static void SetLineHeight( SwTableLine& rLine, SwTwips nOldHeight, SwTwips nNewHeight,
                    bool bMinSize )
{
    SwLayoutFrame* pLineFrame = GetRowFrame( rLine );
    assert(pLineFrame && "Where is the Frame from the SwTableLine?");

    SwFrameFormat* pFormat = rLine.ClaimFrameFormat();

    SwTwips nMyNewH, nMyOldH = pLineFrame->getFrameArea().Height();
    if( !nOldHeight )                       // the BaseLine and absolute
        nMyNewH = nMyOldH + nNewHeight;
    else
    {
        // Calculate as exactly as possible
        Fraction aTmp( nMyOldH );
        aTmp *= Fraction( nNewHeight, nOldHeight );
        aTmp += Fraction( 1, 2 );       // round up if needed
        nMyNewH = tools::Long(aTmp);
    }

    SwFrameSize eSize = SwFrameSize::Minimum;
    if( !bMinSize &&
        ( nMyOldH - nMyNewH ) > ( CalcRowRstHeight(*pLineFrame) + ROWFUZZY ))
        eSize = SwFrameSize::Fixed;

    pFormat->SetFormatAttr( SwFormatFrameSize( eSize, 0, nMyNewH ) );

    // First adapt all internal ones
    for( auto pBox : rLine.GetTabBoxes() )
    {
        for( auto pLine : pBox->GetTabLines() )
            SetLineHeight( *pLine, nMyOldH, nMyNewH, bMinSize );
    }
}

static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
                             SwTwips nDist, bool bCheck )
{
    bool bRet = true;
    if( !bCheck )
    {
        // Set line height
        SetLineHeight( *pLine, 0, rParam.bBigger ? nDist : -nDist,
                        rParam.bBigger );
    }
    else if( !rParam.bBigger )
    {
        // Calculate the new relative size by means of the old one
        SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
        assert(pLineFrame && "Where is the Frame from the SwTableLine?");
        SwTwips nRstHeight = CalcRowRstHeight(*pLineFrame);
        if( (nRstHeight + ROWFUZZY) < nDist )
            bRet = false;
    }
    return bRet;
}

static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
                                SwTwips nDist, bool bCheck )
{
    bool bRet = true;
    if( bCheck )
    {
        if( rParam.bBigger )
        {
            // Calculate the new relative size by means of the old one
            SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
            assert(pLineFrame && "Where is the Frame from the SwTableLine?");

            if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
            {
                nDist *= pLineFrame->getFrameArea().Height();
                nDist /= rParam.nMaxHeight;
            }
            bRet = nDist <= CalcRowRstHeight(*pLineFrame);
        }
    }
    else
    {
        // Set line height
        // pLine is the following/preceding, thus adjust it
        if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
        {
            SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
            assert(pLineFrame && "Where is the Frame from the SwTableLine??");

            // Calculate the new relative size by means of the old one
            // If the selected Box get bigger, adjust via the max space else
            // via the max height.
            if( (true) /*!rParam.bBigger*/ )
            {
                nDist *= pLineFrame->getFrameArea().Height();
                nDist /= rParam.nMaxHeight;
            }
            else
            {
                // Calculate the new relative size by means of the old one
                nDist *= CalcRowRstHeight(*pLineFrame);
                nDist /= rParam.nMaxSpace;
            }
        }
        SetLineHeight( *pLine, 0, rParam.bBigger ? -nDist : nDist,
                        !rParam.bBigger );
    }
    return bRet;
}

bool SwTable::SetRowHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
                        SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo )
{
    SwTableLine* pLine = rCurrentBox.GetUpper();

    SwTableLine* pBaseLine = pLine;
    while( pBaseLine->GetUpper() )
        pBaseLine = pBaseLine->GetUpper()->GetUpper();

    bool bBigger,
        bRet = false,
        bTop = TableChgWidthHeightType::CellTop == extractPosition( eType );
    sal_uInt16 nBaseLinePos = GetTabLines().GetPos( pBaseLine );

    CR_SetLineHeight aParam( eType,
                        const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
    bBigger = aParam.bBigger;

    SwTableLines* pLines = &m_aLines;

    // How do we get to the height?
    switch( extractPosition(eType) )
    {
    case TableChgWidthHeightType::CellTop:
    case TableChgWidthHeightType::CellBottom:
        if( pLine == pBaseLine )
            break;  // it doesn't work then!

        // Is a nested Line (Box!)
        pLines = &pLine->GetUpper()->GetTabLines();
        nBaseLinePos = pLines->GetPos( pLine );
        [[fallthrough]];

    case TableChgWidthHeightType::RowBottom:
        {
            if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
            {
                // First test if we have room at all
                if( bBigger )
                    bRet = true;
                else
                    bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
                                        nAbsDiff, true );

                if( bRet )
                {
                    if( ppUndo )
                        ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));

                    lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
                                    nAbsDiff, false );
                }
            }
            else
            {
                bRet = true;
                SwTableLines::size_type nStt;
                SwTableLines::size_type nEnd;
                if( bTop )
                {
                    nStt = 0;
                    nEnd = nBaseLinePos;
                }
                else
                {
                    nStt = nBaseLinePos + 1;
                    nEnd = pLines->size();
                }

                // Get the current Lines' height
                if( TableChgMode::FixedWidthChangeProp == m_eTableChgMode )
                {
                    for( auto n = nStt; n < nEnd; ++n )
                    {
                        SwLayoutFrame* pLineFrame = GetRowFrame( *(*pLines)[ n ] );
                        assert(pLineFrame && "Where is the Frame from the SwTableLine??");
                        aParam.nMaxSpace += CalcRowRstHeight(*pLineFrame);
                        aParam.nMaxHeight += pLineFrame->getFrameArea().Height();
                    }
                    if( bBigger && aParam.nMaxSpace < nAbsDiff )
                        bRet = false;
                }
                else
                {
                    if( bTop ? nEnd != 0 : nStt < nEnd  )
                    {
                        if( bTop )
                            nStt = nEnd - 1;
                        else
                            nEnd = nStt + 1;
                    }
                    else
                        bRet = false;
                }

                if( bRet )
                {
                    if( bBigger )
                    {
                        for( auto n = nStt; n < nEnd; ++n )
                        {
                            if( !lcl_SetOtherLineHeight( (*pLines)[ n ], aParam,
                                                    nAbsDiff, true ))
                            {
                                bRet = false;
                                break;
                            }
                        }
                    }
                    else
                        bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
                                                nAbsDiff, true );
                }

                if( bRet )
                {
                    // Adjust
                    if( ppUndo )
                        ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));

                    CR_SetLineHeight aParam1( aParam );

                    if( bTop )
                    {
                        lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
                                        nAbsDiff, false );
                        for( auto n = nStt; n < nEnd; ++n )
                            lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1,
                                            nAbsDiff, false );
                    }
                    else
                    {
                        for( auto n = nStt; n < nEnd; ++n )
                            lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1,
                                            nAbsDiff, false );
                        lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
                                        nAbsDiff, false );
                    }
                }
                else
                {
                    // Then call itself recursively; only with another mode (proportional)
                    TableChgMode eOld = m_eTableChgMode;
                    m_eTableChgMode = TableChgMode::VarWidthChangeAbs;

                    bRet = SetRowHeight( rCurrentBox, eType, nAbsDiff,
                                        nRelDiff, ppUndo );

                    m_eTableChgMode = eOld;
                }
            }
        }
        break;
        default: break;
    }

#if defined DBG_UTIL
    CheckTableLayout(GetTabLines());
#endif

    return bRet;
}

SwFrameFormat* SwShareBoxFormat::GetFormat( tools::Long nWidth ) const
{
    SwFrameFormat *pRet = nullptr, *pTmp;
    for( auto n = m_aNewFormats.size(); n; )
        if( ( pTmp = m_aNewFormats[ --n ])->GetFrameSize().GetWidth()
                == nWidth )
        {
            pRet = pTmp;
            break;
        }
    return pRet;
}

SwFrameFormat* SwShareBoxFormat::GetFormat( const SfxPoolItem& rItem ) const
{
    const SfxPoolItem* pItem;
    sal_uInt16 nWhich = rItem.Which();
    SwFrameFormat *pRet = nullptr, *pTmp;
    const SwFormatFrameSize& rFrameSz = m_pOldFormat->GetFormatAttr( RES_FRM_SIZE, false );
    for( auto n = m_aNewFormats.size(); n; )
        if( SfxItemState::SET == ( pTmp = m_aNewFormats[ --n ])->
            GetItemState( nWhich, false, &pItem ) && *pItem == rItem &&
            pTmp->GetFormatAttr( RES_FRM_SIZE, false ) == rFrameSz )
        {
            pRet = pTmp;
            break;
        }
    return pRet;
}

void SwShareBoxFormat::AddFormat( SwFrameFormat& rNew )
{
    m_aNewFormats.push_back( &rNew );
}

bool SwShareBoxFormat::RemoveFormat( const SwFrameFormat& rFormat )
{
    // returns true, if we can delete
    if( m_pOldFormat == &rFormat )
        return true;

    std::vector<SwFrameFormat*>::iterator it = std::find( m_aNewFormats.begin(), m_aNewFormats.end(), &rFormat );
    if( m_aNewFormats.end() != it )
        m_aNewFormats.erase( it );
    return m_aNewFormats.empty();
}

SwShareBoxFormats::~SwShareBoxFormats()
{
}

SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat, tools::Long nWidth ) const
{
    sal_uInt16 nPos;
    return Seek_Entry( rFormat, &nPos )
                    ? m_ShareArr[ nPos ].GetFormat(nWidth)
                    : nullptr;
}
SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat,
                                     const SfxPoolItem& rItem ) const
{
    sal_uInt16 nPos;
    return Seek_Entry( rFormat, &nPos )
                    ? m_ShareArr[ nPos ].GetFormat(rItem)
                    : nullptr;
}

void SwShareBoxFormats::AddFormat( const SwFrameFormat& rOld, SwFrameFormat& rNew )
{
    sal_uInt16 nPos;
    if( !Seek_Entry( rOld, &nPos ))
    {
        SwShareBoxFormat aEntry(rOld);
        aEntry.AddFormat( rNew );
        m_ShareArr.insert(m_ShareArr.begin() + nPos, aEntry);
    }
    else
        m_ShareArr[ nPos ].AddFormat(rNew);
}

void SwShareBoxFormats::ChangeFrameFormat( SwTableBox* pBox, SwTableLine* pLn,
                                    SwFrameFormat& rFormat )
{
    SwClient aCl;
    SwFrameFormat* pOld = nullptr;
    if( pBox )
    {
        pOld = pBox->GetFrameFormat();
        pOld->Add(aCl);
        pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(&rFormat) );
    }
    else if( pLn )
    {
        pOld = pLn->GetFrameFormat();
        pOld->Add(aCl);
        pLn->ChgFrameFormat( static_cast<SwTableLineFormat*>(&rFormat) );
    }
    if( pOld && pOld->HasOnlyOneListener() )
    {
        RemoveFormat( *pOld );
        delete pOld;
    }
}

void SwShareBoxFormats::SetSize( SwTableBox& rBox, const SwFormatFrameSize& rSz )
{
    SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(),
             *pRet = GetFormat( *pBoxFormat, rSz.GetWidth() );
    if( pRet )
        ChangeFrameFormat( &rBox, nullptr, *pRet );
    else
    {
        pRet = rBox.ClaimFrameFormat();
        pRet->SetFormatAttr( rSz );
        AddFormat( *pBoxFormat, *pRet );
    }
}

void SwShareBoxFormats::SetAttr( SwTableBox& rBox, const SfxPoolItem& rItem )
{
    SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(),
             *pRet = GetFormat( *pBoxFormat, rItem );
    if( pRet )
        ChangeFrameFormat( &rBox, nullptr, *pRet );
    else
    {
        pRet = rBox.ClaimFrameFormat();
        pRet->SetFormatAttr( rItem );
        AddFormat( *pBoxFormat, *pRet );
    }
}

void SwShareBoxFormats::SetAttr( SwTableLine& rLine, const SfxPoolItem& rItem )
{
    SwFrameFormat *pLineFormat = rLine.GetFrameFormat(),
             *pRet = GetFormat( *pLineFormat, rItem );
    if( pRet )
        ChangeFrameFormat( nullptr, &rLine, *pRet );
    else
    {
        pRet = rLine.ClaimFrameFormat();
        pRet->SetFormatAttr( rItem );
        AddFormat( *pLineFormat, *pRet );
    }
}

void SwShareBoxFormats::RemoveFormat( const SwFrameFormat& rFormat )
{
    for (auto i = m_ShareArr.size(); i; )
    {
        if (m_ShareArr[ --i ].RemoveFormat(rFormat))
        {
            m_ShareArr.erase( m_ShareArr.begin() + i );
        }
    }
}

bool SwShareBoxFormats::Seek_Entry( const SwFrameFormat& rFormat, sal_uInt16* pPos ) const
{
    sal_uIntPtr nIdx = reinterpret_cast<sal_uIntPtr>(&rFormat);
    auto nO = m_ShareArr.size();
    decltype(nO) nU = 0;
    if( nO > 0 )
    {
        nO--;
        while( nU <= nO )
        {
            const auto nM = nU + ( nO - nU ) / 2;
            sal_uIntPtr nFormat = reinterpret_cast<sal_uIntPtr>(&m_ShareArr[ nM ].GetOldFormat());
            if( nFormat == nIdx )
            {
                if( pPos )
                    *pPos = nM;
                return true;
            }
            else if( nFormat < nIdx )
                nU = nM + 1;
            else if( nM == 0 )
            {
                if( pPos )
                    *pPos = nU;
                return false;
            }
            else
                nO = nM - 1;
        }
    }
    if( pPos )
        *pPos = nU;
    return false;
}

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